functions/MergeDrivers/Merge-D365LabelFile.ps1

<#
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
#>

#Requires -Version 7.0
<#
.SYNOPSIS
    Git merge driver and standalone sorter for AxLabel translation files.
 
.DESCRIPTION
    Merge-driver mode (invoked automatically by git during a merge):
        Performs a 3-way merge of label files, producing an alphabetically sorted
        result. Per-label comments (a line starting with " ;" immediately before
        the label definition) are kept with their label. Unresolvable conflicts are
        written as standard git conflict markers and the script exits 1 so git marks
        the file as conflicted.
 
    Standalone / pipeline mode (no -Base/-Ours/-Theirs supplied):
        Scans the repository for every file matching
            **/AxLabelFile/LabelResources/*/*.label.txt
        and sorts each file's entries alphabetically in-place.
 
    Label file format
    -----------------
        LabelId=value
         ;optional comment ← belongs to the label line before it
 
    Register the merge driver in git config once per clone (or in a pipeline step):
        git config merge.d365fo-label.name "AxLabel file merger"
        git config merge.d365fo-label.driver "pwsh -File D365GitOps/functions/MergeDrivers/Merge-D365LabelFile.ps1 -Base %O -Ours %A -Theirs %B -MarkerSize %L -FilePath %P"
 
    Alternatively, install the D365GitOps module and run:
        Register-D365LabelFileMergeDriver
 
.PARAMETER Base
    [Merge-driver] Ancestor (base) version of the file. Supplied as %O by git.
 
.PARAMETER Ours
    [Merge-driver] Current-branch version. Supplied as %A by git.
    The merged result is written back to this path.
 
.PARAMETER Theirs
    [Merge-driver] Other-branch version. Supplied as %B by git.
 
.PARAMETER MarkerSize
    [Merge-driver] Width of conflict-marker lines. Supplied as %L by git. Default: 7.
 
.PARAMETER FilePath
    [Merge-driver] Repository-relative path of the file. Supplied as %P by git.
    Used only for diagnostic messages.
 
.PARAMETER RepoRoot
    [Standalone] Root directory to search for label files.
    Defaults to the current working directory.
 
.EXAMPLE
    # Run from a pipeline to sort every label file in the repository:
    pwsh -File D365GitOps/functions/MergeDrivers/Merge-D365LabelFile.ps1 -RepoRoot $(Build.SourcesDirectory)
#>


[CmdletBinding(DefaultParameterSetName = 'Standalone')]
param(
    # ── Merge-driver mode ────────────────────────────────────────────────────
    [Parameter(Mandatory, ParameterSetName = 'MergeDriver')]
    [string]$Base,          # %O ancestor version

    [Parameter(Mandatory, ParameterSetName = 'MergeDriver')]
    [string]$Ours,          # %A current-branch version; result written here

    [Parameter(Mandatory, ParameterSetName = 'MergeDriver')]
    [string]$Theirs,        # %B other-branch version

    [Parameter(ParameterSetName = 'MergeDriver')]
    [int]$MarkerSize = 7,   # %L conflict-marker width

    [Parameter(ParameterSetName = 'MergeDriver')]
    [string]$FilePath = '', # %P repo-relative path (informational only)

    # ── Standalone / pipeline mode ────────────────────────────────────────────
    [Parameter(ParameterSetName = 'Standalone')]
    [string]$RepoRoot = (Get-Location).Path
)


if ($MyInvocation.InvocationName -eq '.') {
    return
}

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# ─── Parsing ─────────────────────────────────────────────────────────────────

function Read-LabelEntries {
    [OutputType([System.Collections.Generic.List[pscustomobject]])]
    param([string]$Path)

    $entries   = [System.Collections.Generic.List[pscustomobject]]::new()
    $lastEntry = $null

    foreach ($line in (Get-Content -LiteralPath $Path -Encoding UTF8)) {
        if ($line -match '^ ;') {
            # Comment line – belongs to the previous label definition
            if ($null -ne $lastEntry) {
                $lastEntry.Comment = $line
            }
        }
        elseif ($line -match '^([^=]+)=(.*)$') {
            $entry = [pscustomobject]@{
                Comment = $null
                LabelId = $Matches[1]
                Value   = $Matches[2]
            }
            $entries.Add($entry)
            $lastEntry = $entry
        }
        else {
            # Blank or unrecognised line – reset context
            $lastEntry = $null
        }
    }

    return $entries
}

function Get-EntryMap {
    [OutputType([hashtable])]
    param([System.Collections.Generic.List[pscustomobject]]$Entries)

    $map = [hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($e in $Entries) { $map[$e.LabelId] = $e }
    return $map
}

# ─── Serialising ─────────────────────────────────────────────────────────────

function Format-LabelFile {
    [OutputType([string])]
    param($Entries)

    $lines = [System.Collections.Generic.List[string]]::new()

    foreach ($e in $Entries) {
        if ($e.PSObject.Properties['IsConflict'] -and $e.IsConflict) {
            foreach ($cl in $e.ConflictLines) { $lines.Add($cl) }
            if (![string]::IsNullOrEmpty($e.Comment)) { $lines.Add($e.Comment) }
        }
        else {
            $lines.Add("$($e.LabelId)=$($e.Value)")
            if (![string]::IsNullOrEmpty($e.Comment)) { $lines.Add($e.Comment) }
        }
    }

    # Join with LF; end with a single trailing newline
    return ($lines -join "`n") + "`n"
}

function Save-LabelFile {
    param(
        [string]$Path,
        $Entries
    )

    $text = Format-LabelFile -Entries $Entries
    [System.IO.File]::WriteAllText($Path, $text, [System.Text.UTF8Encoding]::new($false))
}

# ─── Sorting ─────────────────────────────────────────────────────────────────

function Sort-LabelEntries {
    [OutputType([System.Collections.Generic.List[pscustomobject]])]
    param([System.Collections.Generic.List[pscustomobject]]$Entries)

    $sorted = [System.Collections.Generic.List[pscustomobject]]::new()
    $sorted.AddRange([pscustomobject[]]($Entries | Sort-Object -Property LabelId))
    return $sorted
}

# ─── Three-way merge ─────────────────────────────────────────────────────────

# Builds a conflict-marker entry (no actual label value; ConflictLines contains
# the full block to be written verbatim). Either $OursEntry or $TheirsEntry
# may be $null to represent a deletion on that side.
function New-ConflictEntry {
    param(
        [string]$Id,
        [string]$BaseComment,          # may be $null
        [pscustomobject]$OursEntry,    # $null → ours deleted the label
        [pscustomobject]$TheirsEntry,  # $null → theirs deleted the label
        [string]$Lt,
        [string]$Sep,
        [string]$Gt
    )

    $cl = [System.Collections.Generic.List[string]]::new()
    $cl.Add("$Lt ours")
    if ($null -ne $OursEntry) {
        $cl.Add("$Id=$($OursEntry.Value)")
        if (![string]::IsNullOrEmpty($OursEntry.Comment)) { $cl.Add($OursEntry.Comment) }
    }
    $cl.Add($Sep)
    if ($null -ne $TheirsEntry) {
        $cl.Add("$Id=$($TheirsEntry.Value)")
        if (![string]::IsNullOrEmpty($TheirsEntry.Comment)) { $cl.Add($TheirsEntry.Comment) }
    }
    $cl.Add("$Gt theirs")

    return [pscustomobject]@{
        Comment       = $BaseComment
        LabelId       = $Id
        Value         = ''
        IsConflict    = $true
        ConflictLines = $cl
    }
}

function Invoke-LabelMerge {
    param(
        [System.Collections.Generic.List[pscustomobject]]$BaseList,
        [System.Collections.Generic.List[pscustomobject]]$OursList,
        [System.Collections.Generic.List[pscustomobject]]$TheirsList,
        [int]$MarkerSize
    )

    $bMap = Get-EntryMap $BaseList
    $oMap = Get-EntryMap $OursList
    $tMap = Get-EntryMap $TheirsList

    # Union of all label IDs (order here doesn't matter; we sort at the end)
    $allIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($e in $BaseList)   { [void]$allIds.Add($e.LabelId) }
    foreach ($e in $OursList)   { [void]$allIds.Add($e.LabelId) }
    foreach ($e in $TheirsList) { [void]$allIds.Add($e.LabelId) }

    $merged      = [System.Collections.Generic.List[pscustomobject]]::new()
    $conflictIds = [System.Collections.Generic.List[string]]::new()

    $lt  = '<' * $MarkerSize
    $sep = '=' * $MarkerSize
    $gt  = '>' * $MarkerSize

    foreach ($id in $allIds) {
        $inB = $bMap.ContainsKey($id)
        $inO = $oMap.ContainsKey($id)
        $inT = $tMap.ContainsKey($id)

        # ── Not in base: newly added ──────────────────────────────────────────
        if (-not $inB) {
            if ($inO -and -not $inT) { $merged.Add($oMap[$id]); continue }
            if (-not $inO -and $inT) { $merged.Add($tMap[$id]); continue }
            # Added by both
            if ($oMap[$id].Value -eq $tMap[$id].Value) {
                $merged.Add($oMap[$id])
            }
            else {
                $conflictIds.Add($id)
                $merged.Add((New-ConflictEntry $id $null $oMap[$id] $tMap[$id] $lt $sep $gt))
            }
            continue
        }

        # ── In base: check for deletions ─────────────────────────────────────
        if (-not $inO -and -not $inT) {
            # Deleted by both → drop
            continue
        }

        if (-not $inO) {
            # Deleted by ours; theirs is present
            if ($tMap[$id].Value -eq $bMap[$id].Value) {
                # theirs unchanged → accept our deletion
            }
            else {
                # theirs modified it → conflict
                $conflictIds.Add($id)
                $merged.Add((New-ConflictEntry $id $bMap[$id].Comment $null $tMap[$id] $lt $sep $gt))
            }
            continue
        }

        if (-not $inT) {
            # Deleted by theirs; ours is present
            if ($oMap[$id].Value -eq $bMap[$id].Value) {
                # ours unchanged → accept their deletion
            }
            else {
                # ours modified it → conflict
                $conflictIds.Add($id)
                $merged.Add((New-ConflictEntry $id $bMap[$id].Comment $oMap[$id] $null $lt $sep $gt))
            }
            continue
        }

        # ── Present in all three: standard 3-way merge ───────────────────────
        $oChanged = $oMap[$id].Value -ne $bMap[$id].Value
        $tChanged = $tMap[$id].Value -ne $bMap[$id].Value

        if (-not $oChanged -and -not $tChanged) {
            $merged.Add($bMap[$id])
        }
        elseif ($oChanged -and -not $tChanged) {
            $merged.Add($oMap[$id])
        }
        elseif (-not $oChanged -and $tChanged) {
            $merged.Add($tMap[$id])
        }
        elseif ($oMap[$id].Value -eq $tMap[$id].Value) {
            # Both changed to the same value
            $merged.Add($oMap[$id])
        }
        else {
            # True conflict: both changed to different values
            $conflictIds.Add($id)
            $merged.Add((New-ConflictEntry $id $bMap[$id].Comment $oMap[$id] $tMap[$id] $lt $sep $gt))
        }
    }

    return @{
        Entries     = Sort-LabelEntries -Entries $merged
        ConflictIds = $conflictIds
    }
}

# ─── Entry point ─────────────────────────────────────────────────────────────

if ($PSCmdlet.ParameterSetName -eq 'MergeDriver') {

    $displayPath = if ($FilePath) { $FilePath } else { $Ours }
    Write-Host "Merging label file: $displayPath"

    $result = Invoke-LabelMerge `
        -BaseList   (Read-LabelEntries -Path $Base) `
        -OursList   (Read-LabelEntries -Path $Ours) `
        -TheirsList (Read-LabelEntries -Path $Theirs) `
        -MarkerSize $MarkerSize

    Save-LabelFile -Path $Ours -Entries $result.Entries

    if ($result.ConflictIds.Count -gt 0) {
        Write-Warning "Merge conflicts in '$displayPath' for label(s): $($result.ConflictIds -join ', ')"
        exit 1
    }

    exit 0
}

# ── Standalone / pipeline mode ────────────────────────────────────────────────

$labelFiles = @(Get-ChildItem -Path $RepoRoot -Filter '*.label.txt' -Recurse -File |
    Where-Object { $_.FullName -replace '\\', '/' -match '/AxLabelFile/LabelResources/[^/]+/' })

if ($labelFiles.Count -eq 0) {
    Write-Host 'No label files found matching **/AxLabelFile/LabelResources/*/*.label.txt'
    exit 0
}

$changedCount = 0

foreach ($file in $labelFiles) {
    $entries = Read-LabelEntries -Path $file.FullName
    $sorted  = Sort-LabelEntries -Entries $entries
    $before  = Format-LabelFile  -Entries $entries
    $after   = Format-LabelFile  -Entries $sorted

    if ($before -ne $after) {
        Save-LabelFile -Path $file.FullName -Entries $sorted
        Write-Host "Sorted: $($file.FullName)"
        $changedCount++
    }
    else {
        Write-Host "OK: $($file.FullName)"
    }
}

Write-Host ''
Write-Host "Done. $changedCount file(s) updated."

# SIG # Begin signature block
# MII2lQYJKoZIhvcNAQcCoII2hjCCNoICAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBwBd5NXIsoD7jZ
# hBdBfG0R3YdUcRM8PMq/xHRB1srfdaCCGzQwggXMMIIDtKADAgECAhBUmNLR1FsZ
# lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu
# dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy
# MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
# b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD
# Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH
# or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD
# PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV
# qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70
# ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1
# vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3
# W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK
# xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg
# trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj
# O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7
# swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE
# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ
# T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn
# LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c
# Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF
# xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx
# AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8
# FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY
# lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2
# 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY
# uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al
# GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk
# ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggaSMIIEeqADAgECAhMzAAHdaaaY
# 6eAQYJsSAAAAAd1pMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDMwHhcNMjYwNjA4MTgxMjEzWhcNMjYwNjEx
# MTgxMjEzWjBVMQswCQYDVQQGEwJTSTESMBAGA1UEBxMJTGp1YmxqYW5hMRgwFgYD
# VQQKEw9CRS10ZXJuYSBkLm8uby4xGDAWBgNVBAMTD0JFLXRlcm5hIGQuby5vLjCC
# AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJS9/C6Mi/J2DjnLKo8x/yTo
# mfR3mrPDVEa0YSY95IWMnIT8UhSk9PIl/iwLef759Z4mOPOtOT4D93Yln/wdBxwP
# 2qIDF4u4/H3oIA8qHCm7E1a6KMt4xMhm+Fyhwj3Q7bZUOy9LjthNdjR/AJd8xgb6
# eP4iLgAA/j0RusS03WxkO4T41JbfY/h/ldoZv0nVD7iCN75mZsQgH+cqncSOKX7h
# y+2JluyJ22ZFn4QGcbEmg8biWjl3Ox4TKXaJlw2F0ZewBdtSfbuvf3yhiJ9OSk0c
# X9gBKwduhXyBHK/3YxkWOgEA5rJq7yx3512a+JRTIY4kPiQweLzyCWmaQylyr6S3
# OLRC/IevT4ggnoGqPiPN9P2DsqNaGrejIkZ9qM4d+Y5WEA2tYvGpyKWtQ//soo01
# Hdju7cVb/PMf47mR+4MGRKdiq+PRqOSPiSju+tMQBSaxMc1yxcec0e1mdanowyiu
# V/eCl/4Hcpnl7u/woJS/5YhQrrvCeFp2faaISWuaqwIDAQABo4IB1DCCAdAwDAYD
# VR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwOwYDVR0lBDQwMgYKKwYBBAGCN2EB
# AAYIKwYBBQUHAwMGGisGAQQBgjdhpOaoI4H67850gfzolDeutrtWMB0GA1UdDgQW
# BBRwvc4GA2pbi0KnqmADaclVziwBADAfBgNVHSMEGDAWgBSkQwx/dlqlhec+jSgP
# DBeiRWlwxjBnBgNVHR8EYDBeMFygWqBYhlZodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBB
# T0MlMjBDQSUyMDAzLmNybDB0BggrBgEFBQcBAQRoMGYwZAYIKwYBBQUHMAKGWGh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIw
# SUQlMjBWZXJpZmllZCUyMENTJTIwQU9DJTIwQ0ElMjAwMy5jcnQwVAYDVR0gBE0w
# SzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTANBgkqhkiG9w0BAQwFAAOCAgEA
# IOBnoHIENsGe0KoxEFinohJ0YK/l4/Gf/8dGA7dNOWOCdYPHpmdmYFkA9G1bPPoF
# jjGHHIjleggIQt2MPXwoU2Jk0mTHPA+j+mmQgKxLF+n/sAjtlETbKOldNPSHLyep
# PIzFlLWzHlwb/ytd4nv1x6qqkkhQkNGdaRtxgmXtlqvaJUGY0uAh414Bt2AFTzB5
# yLR0VRRTYDzGE3WA9nM91UghYcSbpseOeCmAzWr3k0MHFU87G9ECTk1vY346jII4
# uXeEyf+tmYVPsfFMw0lpAjqjl23SuxiC8oj89CkJ1CDh9268lCFvoVm3gN8oda4s
# 8IS7W0+2w5o4Y7gAElZ1vg5BSK2sb0xXHeze07Udi+Tg7Kx5zZ9Vk00hSzkxZy9L
# uWF5KKyodUCc2eCESqR2moXwZCva8mIsW3yM3HyAgLE2VLVMOZ+kBmVEQsK50l/+
# mgFIMmYZBGJDA3l86rQGlJk5x67UzJYvve6a5K6bBgHkVkPD3IkMOvBjqEPrTbLO
# Nx2sF/zVW4TdZOYEMsHgsdNyzD7vSSCAXwDTMFPWbB8+zbE7QB9v7AXa0Myszp4n
# qXwp0RZOuWceG6jtpVTBtEXISSilknjYUBIWxIs2bixNh0nCo32lIgmFjfpcN1xU
# IS3Cwo3z2SUiIlgjfOGsKk0/0Cp9P6S9AA6dOVwS4gQwggcoMIIFEKADAgECAhMz
# AAAAGA3rkVWpigCYAAAAAAAYMA0GCSqGSIb3DQEBDAUAMGMxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAyBgNVBAMTK01pY3Jv
# c29mdCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIwMjEwHhcNMjYwMzI2
# MTgxMTMyWhcNMzEwMzI2MTgxMTMyWjBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVy
# aWZpZWQgQ1MgQU9DIENBIDAzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAyIDaYDRWoon9lVnlj+SOj5xV8Sf5Qd+3yUeeRgr0exi2QTJAYo24ilcIKQSN
# 8TOZ3+POM5x/6p3Cfjgqust44J0FvkfGXe1Puy45a5nLJGpc0kNIITMRKZwVvPxx
# 7NlfGSc0JOhz/kg7G77C+y3ZR/3jtpeJpJ4QwcK9Gf0Peuk7xLYeW/JAsY9b6ole
# GDbYSxkamUfbtnyv8gTFrvN6ejuLqNhHYPvoBHsOSC+7555yhapkof0fbzyct1hd
# WHGXsAFMfLF2TVJ8d2YVYOfZdi6YrT4sMxOhTKiLKmhL1XtzM7hXdmv7lg2R+lWw
# 8lIkSu/JiINQ0GAPcwxMsgRXDSPp8VUs4Jby+ruz0bjaoHFd7H+hC8cPPcrEDP2e
# EdYURVl0acjliigCrXwR05NFJzYj3MZizDGLPI3lIzonX1T40yK8v1FcJ8MXZZCv
# OXGXwRDGGfwwTTsHaJj+OfWNZ/IsypG4bGvqeJcPnEFcQEwRcfYIEe/R4a8k+xw5
# qTy75CbwWeMFuAlt9lE9kjMg3tvJyDlN5voXx5VXinCwUHMpuVaEQ4yHAlSO7qoB
# ltjzTBNHH3ovMwsAsuhwrLLCVhUu3oP2GxYZwEyXMlnzK5DbgGzHzDfDaYPHK0uo
# 1VaMMg9Bhuc3YIvrkFXEiv+t/JgNcRGCt6ZyKEIDtPbrgwcCAwEAAaOCAdwwggHY
# MA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUpEMM
# f3ZapYXnPo0oDwwXokVpcMYwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUH
# AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0
# b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTASBgNVHRMBAf8ECDAG
# AQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAPD2Ns72nX9c0pnqRIajDmMHAGA1UdHwRp
# MGcwZaBjoGGGX2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUy
# MDIwMjEuY3JsMH0GCCsGAQUFBwEBBHEwbzBtBggrBgEFBQcwAoZhaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJRCUyMFZl
# cmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDIxLmNydDANBgkqhkiG
# 9w0BAQwFAAOCAgEAcccgVvl+poXUYksA/TzDFnBlAJ8ef0FMJzb2XRRhF/uA0QyK
# /VgoeAvO8B7cPpYNQ97sytdA7LT19CxSwRQAt71jGF+CJl8KC4aEdMZTfJlHaKyd
# 24J6QiVriNed9WdawsD7lK0pAcXziBg5N6dhAm9x6P8R4uT0UkfzlK1rkB8F4mlz
# E7l7tyES3s8FZGaRZjcGEQ+e0fTcdhf8jO7czmNB4dIRgmmBCt/P+ha0tEl2nV1s
# g1An5+VzhgAkY1Apx8fiUFBtH+Ehw/om5aQCNIJfmR51ZnV18R02Xk2tAmAiIRcS
# j9vdtrNIOsy5nolddy1lJrbf1Be061l6TItv9FDZ4mg6B+65zxkVecVV/Ll8uLGY
# ouGrMM6jzO2O/ps3K2p6mfBI2ZOYIy4UNwNrGWqa5TrvAmkZsn3CIlR+81X4AL5v
# NTFlxc4gH+5su0Dr58hBTxnXavDEnz7X0csP1Kt7h+iqaGiTSHz2B+n3HmUoud0W
# rdQPYKxMat0To4YUqU3HIbgSLQDDVT8aCjW1Jvokf1915C/vVkIIp48h3voVy3JW
# PLwBlxQ9aeND6jCKQGLJhCQRSlvXX+P/9TeaEA6/xWPSASZf6Ekve/Yua7U+zWc/
# Sr2K2gj0QRrNEAsvrFr4EGtHKDO9ECVS3lcJksVDv9KHdMPUK8u20i68RqAwggee
# MIIFhqADAgECAhMzAAAAB4ejNKN7pY4cAAAAAAAHMA0GCSqGSIb3DQEBDAUAMHcx
# CzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBG
# BgNVBAMTP01pY3Jvc29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0
# aWZpY2F0ZSBBdXRob3JpdHkgMjAyMDAeFw0yMTA0MDEyMDA1MjBaFw0zNjA0MDEy
# MDE1MjBaMGMxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xNDAyBgNVBAMTK01pY3Jvc29mdCBJRCBWZXJpZmllZCBDb2RlIFNpZ25p
# bmcgUENBIDIwMjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCy8MCv
# GYgo4t1UekxJbGkIVQm0Uv96SvjB6yUo92cXdylN65Xy96q2YpWCiTas7QPTkGnK
# 9QMKDXB2ygS27EAIQZyAd+M8X+dmw6SDtzSZXyGkxP8a8Hi6EO9Zcwh5A+wOALNQ
# bNO+iLvpgOnEM7GGB/wm5dYnMEOguua1OFfTUITVMIK8faxkP/4fPdEPCXYyy8NJ
# 1fmskNhW5HduNqPZB/NkWbB9xxMqowAeWvPgHtpzyD3PLGVOmRO4ka0WcsEZqyg6
# efk3JiV/TEX39uNVGjgbODZhzspHvKFNU2K5MYfmHh4H1qObU4JKEjKGsqqA6Rzi
# ybPqhvE74fEp4n1tiY9/ootdU0vPxRp4BGjQFq28nzawuvaCqUUF2PWxh+o5/TRC
# b/cHhcYU8Mr8fTiS15kRmwFFzdVPZ3+JV3s5MulIf3II5FXeghlAH9CvicPhhP+V
# aSFW3Da/azROdEm5sv+EUwhBrzqtxoYyE2wmuHKws00x4GGIx7NTWznOm6x/niqV
# i7a/mxnnMvQq8EMse0vwX2CfqM7Le/smbRtsEeOtbnJBbtLfoAsC3TdAOnBbUkbU
# fG78VRclsE7YDDBUbgWt75lDk53yi7C3n0WkHFU4EZ83i83abd9nHWCqfnYa9qIH
# PqjOiuAgSOf4+FRcguEBXlD9mAInS7b6V0UaNwIDAQABo4ICNTCCAjEwDgYDVR0P
# AQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTZQSmwDw9jbO9p
# 1/XNKZ6kSGow5jBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRt
# MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYD
# VR0jBBgwFoAUyH7SaoUqG8oZmAQHJ89QEE9oqKIwgYQGA1UdHwR9MHsweaB3oHWG
# c2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIw
# QXV0aG9yaXR5JTIwMjAyMC5jcmwwgcMGCCsGAQUFBwEBBIG2MIGzMIGBBggrBgEF
# BQcwAoZ1aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNy
# b3NvZnQlMjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZp
# Y2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAuY3J0MC0GCCsGAQUFBzABhiFodHRwOi8v
# b25lb2NzcC5taWNyb3NvZnQuY29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAH8l
# Kp7+1Kvq3WYK21cjTLpebJDjW4ZbOX3HD5ZiG84vjsFXT0OB+eb+1TiJ55ns0BHl
# uC6itMI2vnwc5wDW1ywdCq3TAmx0KWy7xulAP179qX6VSBNQkRXzReFyjvF2BGt6
# FvKFR/imR4CEESMAG8hSkPYso+GjlngM8JPn/ROUrTaeU/BRu/1RFESFVgK2wMz7
# fU4VTd8NXwGZBe/mFPZG6tWwkdmA/jLbp0kNUX7elxu2+HtHo0QO5gdiKF+YTYd1
# BGrmNG8sTURvn09jAhIUJfYNotn7OlThtfQjXqe0qrimgY4Vpoq2MgDW9ESUi1o4
# pzC1zTgIGtdJ/IvY6nqa80jFOTg5qzAiRNdsUvzVkoYP7bi4wLCj+ks2GftUct+f
# GUxXMdBUv5sdr0qFPLPB0b8vq516slCfRwaktAxK1S40MCvFbbAXXpAZnU20FaAo
# Dwqq/jwzwd8Wo2J83r7O3onQbDO9TyDStgaBNlHzMMQgl95nHBYMelLEHkUnVVVT
# UsgC0Huj09duNfMaJ9ogxhPNThgq3i8w3DAGZ61AMeF0C1M+mU5eucj1Ijod5O2M
# MPeJQ3/vKBtqGZg4eTtUHt/BPjN74SsJsyHqAdXVS5c+ItyKWg3Eforhox9k3Wgt
# WTpgV4gkSiS4+A09roSdOI4vrRw+p+fL4WrxSK5nMYIatzCCGrMCAQEwcTBaMQsw
# CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYD
# VQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgQU9DIENBIDAzAhMzAAHdaaaY
# 6eAQYJsSAAAAAd1pMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOHvyJBHEKkSCCJ/L9vV
# S99gCUW5SzkWuBHsaXdjRk//MA0GCSqGSIb3DQEBAQUABIIBgDQN+KtbznY7slah
# E5f+xUF8UybFsT/LNbUBAL+sxUuvAeAT7hrd5lpwVzHPTuaj+dVpiwdQ7+XkXtAW
# hvt23a3AgKt5lWoMvNJbXD/sYwnki9VQPM+gQf/50HuUkoM5wEagp0z86FiJpaE4
# xULQwixLl1f+NtxBXopHYvTPvrtg3pRmJFhm4OYQoQwHC/SI3MSOqTCOkiArQKUT
# qkU2zVnpO4gSdWgzw9UTBWEzaqe5xSuU5rDPCd5BzOBXOOnaLjgBymN/XWtenscw
# pZCDWEHnH83IF6ZdefZSKhfW7ipVssYd7L1kqJOEncoWAALC8VUe7K3PmaUPWIbf
# X5Rd00sCAkKRMEQsFEY9XRUNTrW0QZjv/Fss21WKUo0+LsQH6ys8bTHFTggDFsQl
# hkpxqNFyTt3qOTZ7+BPKTS59o5SV7bLPhK/lZrqOgH/C5rdahJlx68KsZ1QUo/n9
# bNuXj+wgBOMTOeDMcDd3eOLtstKYc2izhN54qZHriHNdhsrzO6GCGBAwghgMBgor
# BgEEAYI3AwMBMYIX/DCCF/gGCSqGSIb3DQEHAqCCF+kwghflAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFhBgsqhkiG9w0BCRABBKCCAVAEggFMMIIBSAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCAcwB5f7IcB7hXAHFiIzW78OJT5hWAZ6I5k
# NNc6qazjoAIGahxW5PxNGBIyMDI2MDYwOTIyMTYzOS43NlowBIACAfSggeGkgd4w
# gdsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsT
# HE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQg
# VFNTIEVTTjpBNTAwLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0IFB1Ymxp
# YyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmggg8hMIIHgjCCBWqgAwIBAgIT
# MwAAAAXlzw//Zi7JhwAAAAAABTANBgkqhkiG9w0BAQwFADB3MQswCQYDVQQGEwJV
# UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMUgwRgYDVQQDEz9NaWNy
# b3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJvb3QgQ2VydGlmaWNhdGUgQXV0
# aG9yaXR5IDIwMjAwHhcNMjAxMTE5MjAzMjMxWhcNMzUxMTE5MjA0MjMxWjBhMQsw
# CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD
# VQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ5851Jj/eDFnwV9Y7UGIqMc
# HtfnlzPREwW9ZUZHd5HBXXBvf7KrQ5cMSqFSHGqg2/qJhYqOQxwuEQXG8kB41wsD
# JP5d0zmLYKAY8Zxv3lYkuLDsfMuIEqvGYOPURAH+Ybl4SJEESnt0MbPEoKdNihwM
# 5xGv0rGofJ1qOYSTNcc55EbBT7uq3wx3mXhtVmtcCEr5ZKTkKKE1CxZvNPWdGWJU
# PC6e4uRfWHIhZcgCsJ+sozf5EeH5KrlFnxpjKKTavwfFP6XaGZGWUG8TZaiTogRo
# AlqcevbiqioUz1Yt4FRK53P6ovnUfANjIgM9JDdJ4e0qiDRm5sOTiEQtBLGd9Vhd
# 1MadxoGcHrRCsS5rO9yhv2fjJHrmlQ0EIXmp4DhDBieKUGR+eZ4CNE3ctW4uvSDQ
# VeSp9h1SaPV8UWEfyTxgGjOsRpeexIveR1MPTVf7gt8hY64XNPO6iyUGsEgt8c2P
# xF87E+CO7A28TpjNq5eLiiunhKbq0XbjkNoU5JhtYUrlmAbpxRjb9tSreDdtACpm
# 3rkpxp7AQndnI0Shu/fk1/rE3oWsDqMX3jjv40e8KN5YsJBnczyWB4JyeeFMW3JB
# fdeAKhzohFe8U5w9WuvcP1E8cIxLoKSDzCCBOu0hWdjzKNu8Y5SwB1lt5dQhABYy
# zR3dxEO/T1K/BVF3rV69AgMBAAGjggIbMIICFzAOBgNVHQ8BAf8EBAMCAYYwEAYJ
# KwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGtpKDo1L0hjQM972K9J6T7ZPdshMFQG
# A1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYI
# KwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUw
# AwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0w
# ezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWlj
# cm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlm
# aWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQw
# gYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Nl
# cnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUy
# MENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwDQYJKoZIhvcNAQEM
# BQADggIBAF+Idsd+bbVaFXXnTHho+k7h2ESZJRWluLE0Oa/pO+4ge/XEizXvhs0Y
# 7+KVYyb4nHlugBesnFqBGEdC2IWmtKMyS1OWIviwpnK3aL5JedwzbeBF7POyg6IG
# G/XhhJ3UqWeWTO+Czb1c2NP5zyEh89F72u9UIw+IfvM9lzDmc2O2END7MPnrcjWd
# QnrLn1Ntday7JSyrDvBdmgbNnCKNZPmhzoa8PccOiQljjTW6GePe5sGFuRHzdFt8
# y+bN2neF7Zu8hTO1I64XNGqst8S+w+RUdie8fXC1jKu3m9KGIqF4aldrYBamyh3g
# 4nJPj/LR2CBaLyD+2BuGZCVmoNR/dSpRCxlot0i79dKOChmoONqbMI8m04uLaEHA
# v4qwKHQ1vBzbV/nG89LDKbRSSvijmwJwxRxLLpMQ/u4xXxFfR4f/gksSkbJp7oqL
# wliDm/h+w0aJ/U5ccnYhYb7vPKNMN+SZDWycU5ODIRfyoGl59BsXR/HpRGtiJquO
# YGmvA/pk5vC1lcnbeMrcWD/26ozePQ/TWfNXKBOmkFpvPE8CH+EeGGWzqTCjdAsn
# o2jzTeNSxlx3glDGJgcdz5D/AAxw9Sdgq/+rY7jjgs7X6fqPTXPmaCAJKVHAP19o
# EjJIBwD1LyHbaEgBxFCogYSOiUIr0Xqcr1nJfiWG2GwYe6ZoAF1bMIIHlzCCBX+g
# AwIBAgITMwAAAFZ+j51YCI7pYAAAAAAAVjANBgkqhkiG9w0BAQwFADBhMQswCQYD
# VQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQD
# EylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDAeFw0y
# NTEwMjMyMDQ2NTFaFw0yNjEwMjIyMDQ2NTFaMIHbMQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP
# cGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTUwMC0wNUUwLUQ5
# NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcg
# QXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtKWfm/ul
# 027/d8Rlb8Mn/g0QUvvLqY2Vsy3tI8U2tFSspTZomZOD3BHT8LkR+RrhMJgb1VjA
# KFNysaK9cLSXifPGSIBrPCgs9P4y24lrJEmrV6Q5z4BmqMhIPrZhEvZnWpCS4HO7
# jYSei/nxmC7/1Er+l5Lg3PmSxb8d2IVcARxSw1B4mxB6XI0nkel9wa1dYb2wfGpo
# fraFmxZOxT9eNht4LH0RBSVueba6ZNpjS/0gtfm7qiIiyP6p6PRzTTbMnVqsHnV/
# d/rW0zHx+Q+QNZ5wUqKmTZJB9hU853+2pX5rDfK32uNY9/WBOAmzbqgpEdQkbiMa
# vUMyUDShmycIvgHdQnS207sTj8M+kJL3tOdahPuPqMwsaCCgdfwwQx0O9TKe7FSv
# bAEYs1AnldCl/KHGZCOVvUNqjyL10JLe0/+GD9/ynqXGWFpXOjaunvZ/cKROhjN4
# M5e6xx0b2miqcPii4/ii2ZheKallJET7CKlpFShs3wyg6F/fojQxQvPnbWD4Nyx6
# lhjWjwmoLcx6w1FSCtavLCly33BLRSlTU4qKUxaa8d7YN7Eqpn9XO0SY0umOvKFX
# rWH7rxl+9iaicitdnTTksAnRjvekdKT3lg7lRMfmfZU8vXNiN0UYJzT9EjqjRm0u
# N/h0oXxPhNfPYqeFbyPXGGxzaYUz6zx3qTcCAwEAAaOCAcswggHHMB0GA1UdDgQW
# BBS+tjPyu6tZ/h5GsyLvyz1H+FNIWjAfBgNVHSMEGDAWgBRraSg6NS9IY0DPe9iv
# Sek+2T3bITBsBgNVHR8EZTBjMGGgX6BdhltodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBQdWJsaWMlMjBSU0ElMjBUaW1lc3Rh
# bXBpbmclMjBDQSUyMDIwMjAuY3JsMHkGCCsGAQUFBwEBBG0wazBpBggrBgEFBQcw
# AoZdaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3Nv
# ZnQlMjBQdWJsaWMlMjBSU0ElMjBUaW1lc3RhbXBpbmclMjBDQSUyMDIwMjAuY3J0
# MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/
# BAQDAgeAMGYGA1UdIARfMF0wUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTAIBgZngQwBBAIwDQYJKoZIhvcNAQEMBQADggIBAA4DqAXEsO26j/La7Fgn
# /Qifit8xuZekqZ57+Ye+sH/hRTbEEjGYrZgsqwR/lUUfKCFpbZF8msaZPQJOR4YY
# UEU8XyjLrn8Y1jCSmoxh9l7tWiSoc/JFBw356JAmzGGxeBA2EWSxRuTr1AuZe6nY
# aN8/wtFkiHcs8gMadxXBs6DxVhyu5YnhLPQkfumKm3lFftwE7pieV7f1lskmlgsC
# 6AeSGCzGPZUgCvcH5Tv/Qe9z7bIImSD3SuzhOIwaP+eKQTYf67TifyJKkWQSdGfT
# A6Kcu41k8LB6oPK+MLk1jbxxK5wPqLSL62xjK04SBXHEJSEnsFt0zxWkxP/lgej1
# DxqUnmrYEdkxvzKSHIAqFWSZul/5hI+vJxvFPhsNQBEk4cSulDkJQpcdVi/gmf/m
# HFOYhDBjsa15s4L+2sBil3XV/T8RiR66Q8xYvTLRWxd2dVsrOoCwnsU4WIeiC0Ji
# nCv1WLHEh7Qyzr9RSr4kKJLWdpNYLhgjkojTmEkAjFO774t3xB7enbvIF0GOsV19
# xnCUzq9EGKyt0gMuaphKlNjJ+aTpjWMZDGo+GOKsnp93Hmftml0Syp3F9+M3y+y6
# WJGUZoIZJq227jDjjEndtpUrh9BdPdVIfVJD/Au81Rzh05UHAivorQ3Os8PELHIg
# iOd9TWzbdgmGzcILt/ddVQERMYIHQzCCBz8CAQEweDBhMQswCQYDVQQGEwJVUzEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMAITMwAAAFZ+j51YCI7p
# YAAAAAAAVjANBglghkgBZQMEAgEFAKCCBJwwEQYLKoZIhvcNAQkQAg8xAgUAMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwNjA5
# MjIxNjM5WjAvBgkqhkiG9w0BCQQxIgQgLWzfQOYu0rGMtInkBJWgAeWKnzwsBGj2
# 92xQaQA3G8QwgbkGCyqGSIb3DQEJEAIvMYGpMIGmMIGjMIGgBCC2DDMlTaTj8JV3
# iTg5Xnpe4CSH60143Z+X9o5NBgMMqDB8MGWkYzBhMQswCQYDVQQGEwJVUzEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# UHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMAITMwAAAFZ+j51YCI7pYAAA
# AAAAVjCCA14GCyqGSIb3DQEJEAISMYIDTTCCA0mhggNFMIIDQTCCAikCAQEwggEJ
# oYHhpIHeMIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw
# IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5u
# U2hpZWxkIFRTUyBFU046QTUwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29m
# dCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5oiMKAQEwBwYFKw4D
# AhoDFQD/c/cpFSqQWYBeXggyRJ2ZbvYEEaBnMGWkYzBhMQswCQYDVQQGEwJVUzEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDANBgkqhkiG9w0BAQsF
# AAIFAO3SstowIhgPMjAyNjA2MDkxNTQyMThaGA8yMDI2MDYxMDE1NDIxOFowdDA6
# BgorBgEEAYRZCgQBMSwwKjAKAgUA7dKy2gIBADAHAgEAAgIT0TAHAgEAAgISgTAK
# AgUA7dQEWgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQCXAXMX1U7tFncE
# b62A4vxpOiXij4M1aegoWk/Q43cIgsAGy5yuwHvhF92H7+vZDZP4VVG353vXw0fJ
# kDBq9+WVwr3t7mXPg/Cnn1QVm7ZqmkQGlNJvRzcoEWZYYJeu8bl0goZb9QI4Px9h
# FyDJfXBTy/ZWlBQrP1JeFDFls6CSGExzDqD6rtKVG4CD7UmwwV1tLO5tCCNhKtbB
# seoue5wglEwcJxmWGmPVWbNnQZyIUsQWHaMmpS73GeYg+07BxrjwEcXMDmb3sGWf
# N+GFIltTGuCvGjmm/j43DywFxDKd1C/D5Rwur+NXCxqYOIsGQI4RDieLIxVWe5mk
# Zvd5ou72MA0GCSqGSIb3DQEBAQUABIICABwf7vqgDBxxO6lk2qEERgpeAmH2hsY9
# lmVifj/QD4be2K+Zcf9oWU6AByNkcy7v7R4WpWC05qdQYceQnlveZu5VlThyXlbr
# hJRjhnPIDQkJDMLyQmr02k1MjfVZ7VLPvd9tz7bkRAfJoJUiGZPYODGuLiMB57H4
# 70x5wgjraBJmxDQqL0Jl2yHtxgLzHbhPIrSkM2DI6MSOwBtHBJlVVZ9mdgXZSDSm
# wmT1jnV6t+NKjnkUCvy6/uzPLheE/jDPIOnv1UIDmhWSO1WfL0kP0YuT/n4ZrNjq
# dEJL4TbvNWzDaEvZWoWaxJiSA4RyiKSr6c9lL+a5wzMCQiRNifNzt/HQImR9HRNH
# f9Dx2/n2A3NE8CREgpgCopHOQcGL7yvNNv2QDpBHOorEIhjn3C/G1i70Z0TC3jPZ
# cwDGk4n/QMWzTaz3wKmCrySqWL59lwGFAhfY+AAba9o4wJ/Ski0EcjWD3JOorLRm
# tzOiCUxYXMxX5FPWfcaJK/+xCfPXVQ7BX3dHHAkaOb58IQXkaDQ98yz7lJJIfp7P
# zBo/3ujFuinJR+0z8IH2kccSeSOixlhM6vIg4wB+Srxt4U8C9SJEEH1GuqJ/4Wv3
# ezA9KkRKh6reXP0HCw21HwBbsKE17Edj/uMWzLPtP1rvr5NfORL3s2y+5EAIrT9m
# ZyCUhRH1SGo+
# SIG # End signature block