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
# MII2mQYJKoZIhvcNAQcCoII2ijCCNoYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# 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
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggaSMIIEeqADAgECAhMzAAHxvjzX
# QAAWrnYGAAAAAfG+MA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwNjE0MTgxODQzWhcNMjYwNjE3
# MTgxODQzWjBVMQswCQYDVQQGEwJTSTESMBAGA1UEBxMJTGp1YmxqYW5hMRgwFgYD
# VQQKEw9CRS10ZXJuYSBkLm8uby4xGDAWBgNVBAMTD0JFLXRlcm5hIGQuby5vLjCC
# AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAIpDLpP0SRhDOd8sX2jFT9zM
# lZoFHWK/NfQkJZIrkG0anaOIo0AZbwvJ2oqHnWCEKGEgCBWU1QaD9iKLr6Ah+cBH
# JUrfjKtU/KpH0ij3KdGCx/y0hrcRZq3j1wo+vcySXQrAYqxujmMg7qan6+GLHQ55
# fVdmccem2iEpEyw4jYseMRdMroKvAFCBksI+Dgi5nPckn8fJH4bZmUj65KWOJhn3
# n/B4i+oWyP1EOGgBfvm5jFs8BW1Z5a9eLXS8+cC0GV+9Mkn6OtT9VUJj2tMu2Jun
# GLOww1V0nMteqVGt4nZ6n6QUXEcvK1nPrUK19nkZb26en1ZSlgGBwS/TR+ti9G/m
# mFSOZAfkAMTxFNM0VZrdgNUwy1Ginh/+Uy0Mn8ku026GuXUWykjiCNgc7aNuNBCo
# xuaibKbiyCYikDVK1C49sySqyfTN+eJ9PYXP8ve9vcoxMU4oWUvt46/PDzsifUWE
# zes8L9ufzHuPKudofIMowfJSJ7U3jZTY2F0PNXdS6wIDAQABo4IB1DCCAdAwDAYD
# VR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwOwYDVR0lBDQwMgYKKwYBBAGCN2EB
# AAYIKwYBBQUHAwMGGisGAQQBgjdhpOaoI4H67850gfzolDeutrtWMB0GA1UdDgQW
# BBQUDZ5ikhrjjetzP6/ke2d4tfTf4DAfBgNVHSMEGDAWgBSa8VR3dQyHFjdGoKze
# efn0f8F46TBnBgNVHR8EYDBeMFygWqBYhlZodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBF
# T0MlMjBDQSUyMDA0LmNybDB0BggrBgEFBQcBAQRoMGYwZAYIKwYBBQUHMAKGWGh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIw
# SUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0ElMjAwNC5jcnQwVAYDVR0gBE0w
# SzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTANBgkqhkiG9w0BAQwFAAOCAgEA
# WQa/CzxHrrk6I+nb3lG4Ug1cH7rwJhXOSArmI4jEa0iJ9Hu5DsSqjGFuuXJB9ZKW
# m4pzWnu1XCduK9aVzgs4pWWN/PLJk39MoBmyP1hErYWHY9g3DMM0B/4Ue1z8OpCg
# htr6lfO2DxPoMJJY2qu4cjrh4nUFpzsvBc1cQdZjQxQp3fSbg2ZxA+oKbrZsUc1R
# rbxKwMdrUehb5AiBAPmgzK7pAMbN++wvnvVc3FEnYGcskorJKJSs/HmOh0G0uNDS
# ZxsPgX6ddcMTPyAHAP82tStH3RgEVjLE0s98l/Z9uOuHc/JOxkUGm58ulnNdvyV1
# xBajAQoUT2ug1wPjFWojqOAvT666wSCQZYVsgaI52uArI8b8ZOLCjwIT5Ik2YTfk
# WWjSF8YNuYwyglz2q95rlvL4XiwKTsePBurHzH8mnfrEw4hMCn+ZihkdloE5DYg7
# vvHHa6k9pSlNougHkdp6xqXOftBq2b082CtM+EzZNdOCnkqDX2i1n0Zx37JRSHfQ
# e9uCKXF7ZxEFGeltJjiWcxzyYi5KYHr/T2yqTi/BWW0uHOPVzO1hf6/o3MSRwz2/
# 9qrnxlinoTZOeEhR14VXxQa+euAN1FNiC2m1tz3JoTDNX31OoLpdL1khGMwl8VIV
# K5aGHFrqoNcgWa65tzq4d6GwnRQT7DrVkMgktOtD7BQwggcoMIIFEKADAgECAhMz
# AAAAFydFCQuLh6/GAAAAAAAXMA0GCSqGSIb3DQEBDAUAMGMxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAyBgNVBAMTK01pY3Jv
# c29mdCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIwMjEwHhcNMjYwMzI2
# MTgxMTMxWhcNMzEwMzI2MTgxMTMxWjBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVy
# aWZpZWQgQ1MgRU9DIENBIDA0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAgsdk/gMPZioBlcyfk6tDzJ+PRt4rSLGKW8ewpS0kRxXtURC3T3GdbCKljobE
# n8ussqhGqQpRh/SXvRVwNXEIGb76UG5IPkCJ1S6/9BD61QQsKzPepW0SNj8TXgsF
# xvS7MltoRuikIIp7Q5jQgaOM6QyK9++6ZVXUpYmZulAe6x8JrwZ0dNkE+rZ66lqt
# oocwepUSVUxM7odDmn8yDHjJ2DNPsfr3uRDix3X4qvh14jH/SW+2Cx7WIMhyIiQO
# 201i6hUixmk4e2ZW8W7C1wPdTjq6BKb+zo8xbrt7ZKQvRX5QOA6dhLquPqj5sVKn
# xqfk19IC0SafTSTs8yC43Ew965BRRW8VL9ccoOmr4rxQy7aCgYTNk3dd/LphNaTT
# mnGp7kmLTxyHkB5geoWhYuuGrywS8E0wJv0W4rfOtHBV0e9sKvuUIeIUpnsx6ilx
# EVj6VQXvgD6yeCKnPmj3jJiJKAlmUDtth5yzRVBUl44sMiG4L5R/yyACRKk2n088
# Q2YCoZS1O86+oMLKt1jaXGECOjbsVp8Id1VQw8he6J0KirOS5e25XlTdGPFb6oBO
# OaacgW78Kjf0bp+XzAgkc92mDGNJGYSjvdnj+7eMx6meW0DAIGdLRNj8/429MIsp
# FBfz3KDqqpN71S4kQ2LLer3dxhDDczKVFL0HLwRuOvgjiG8CAwEAAaOCAdwwggHY
# MA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUmvFU
# d3UMhxY3RqCs3nn59H/BeOkwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUH
# AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0
# b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTASBgNVHRMBAf8ECDAG
# AQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAPD2Ns72nX9c0pnqRIajDmMHAGA1UdHwRp
# MGcwZaBjoGGGX2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUy
# MDIwMjEuY3JsMH0GCCsGAQUFBwEBBHEwbzBtBggrBgEFBQcwAoZhaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJRCUyMFZl
# cmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDIxLmNydDANBgkqhkiG
# 9w0BAQwFAAOCAgEAkHVaGf1NJt/JdoimmRZbMWr6baaDi8mkdWvWStk0hdZDpxSY
# TA7HuipAoLL3qIhI101XOl7fOiCh5++jZOamQdAV79ojEUNoIgCZmL2XJrLaGanw
# djNynecJyYVCTrRf2+h7KknpWOp4axdOs6K9ZQ5g0IsQWXCwfc0dfkSkLKNY3pDc
# WLlJPh2jd5NUue6pNDv/2G5MFNJhCwltODebyAjGceU+XOzav+7i721YQnQ+39m2
# aQOFO7zpAdaKAeAGhEd6Y6CdDGneSxcoujWvafWbv4ay3jo1ORSLUuWMbKr5X18Q
# E4Sde+gppGLLSkZsrUh2eyYSkX1envWX7ZPzg2/wiuKRlQFarDn+N9+20Bqzhxwk
# NyLzfYJp1Lg4fCXb24XqFjx8SDdRgebFImOfOLVze8XQ/CwkrEaib0PHu2t4GVk4
# FYroEbNUFqvjdBvTY3uiR5TdQoyXoYHvh+TxpLSY2vo7hhK9D/rpEpHC+qmmcRUE
# 4d0gyO9Zb1vvt25fxM3ekjvDfVHcPq3qMr0Rwsk4krKZWUEgU1SXT5qN6gqRrshx
# bT6OQgZ9/xT04qiXdzPQR6KindBvSpoOnxnALxcJyzVwNpKL+9u8EZYy98qX6i+4
# gE/2J6cbpekcB0ZXDn/XQxoNUUb6/djT/wllVyG+vIHkdq71PzbH5rYxdcAwggee
# 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+fL4WrxSK5nMYIauzCCGrcCAQEwcTBaMQsw
# CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYD
# VQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENBIDA0AhMzAAHxvjzX
# QAAWrnYGAAAAAfG+MA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOHvyJBHEKkSCCJ/L9vV
# S99gCUW5SzkWuBHsaXdjRk//MA0GCSqGSIb3DQEBAQUABIIBgBOKrSjMRf3ipAWC
# T1Zy1+XhGf7b8uxfMMrkqZiJWTPheODgpSybyJuGSqKQfcCdj6L8DOGacjyrNlDN
# ZLEE0f5pBPOv8ehlLmeDl8aBLxnChHuSDtRRbrHHF1/gj6Pq+yEM1kHvI6VabrE/
# c62LC3PZVmihI/UH9ItdaHkyHXXRN/LGK695Ol0/uryytjGLX4YgoAnTxwP0VyfF
# 7lxsZqN3u5qX+fJAHT5HY0P4LLpgBgrO3KrKuZhiySZclF2Waohr92FYWtEuq2qX
# Ax3l2eZzaPkFsDhFE3ezTTvTKDyX/3+otRaFrjH7fgJukNwISGOtDWuk8le6F5ZI
# FnUiqVF5ViHjLoLKNf3kFdw/lbQxlV3BuoG/P0jW0z4Vf9xBWkbHuSM0BO56Aehr
# lEtpf2sA78UoBT+1lFy7EGwLmw9rlANwLoPc9Vqv1IElGb0OO+9CHzCcjyhJ0n5r
# j+fx1+Wxt+i3AN3b8mC75DwB9gwRjOOHKHMAfztB7QjWvn7ZeqGCGBQwghgQBgor
# BgEEAYI3AwMBMYIYADCCF/wGCSqGSIb3DQEHAqCCF+0wghfpAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFiBgsqhkiG9w0BCRABBKCCAVEEggFNMIIBSQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCABAkFUa0FKj0s90ZBbWfmC7DvzTDqjrVr0
# Nv2NY+ZnogIGaeiBbxzrGBMyMDI2MDYxNTIyMTkwOS4xNDFaMASAAgH0oIHhpIHe
# MIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL
# ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk
# IFRTUyBFU046N0QwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJs
# aWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5oIIPITCCB4IwggVqoAMCAQIC
# EzMAAAAF5c8P/2YuyYcAAAAAAAUwDQYJKoZIhvcNAQEMBQAwdzELMAkGA1UEBhMC
# VVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjFIMEYGA1UEAxM/TWlj
# cm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBSb290IENlcnRpZmljYXRlIEF1
# dGhvcml0eSAyMDIwMB4XDTIwMTExOTIwMzIzMVoXDTM1MTExOTIwNDIzMVowYTEL
# MAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG
# A1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCefOdSY/3gxZ8FfWO1BiKj
# HB7X55cz0RMFvWVGR3eRwV1wb3+yq0OXDEqhUhxqoNv6iYWKjkMcLhEFxvJAeNcL
# AyT+XdM5i2CgGPGcb95WJLiw7HzLiBKrxmDj1EQB/mG5eEiRBEp7dDGzxKCnTYoc
# DOcRr9KxqHydajmEkzXHOeRGwU+7qt8Md5l4bVZrXAhK+WSk5CihNQsWbzT1nRli
# VDwunuLkX1hyIWXIArCfrKM3+RHh+Sq5RZ8aYyik2r8HxT+l2hmRllBvE2Wok6IE
# aAJanHr24qoqFM9WLeBUSudz+qL51HwDYyIDPSQ3SeHtKog0ZubDk4hELQSxnfVY
# XdTGncaBnB60QrEuazvcob9n4yR65pUNBCF5qeA4QwYnilBkfnmeAjRN3LVuLr0g
# 0FXkqfYdUmj1fFFhH8k8YBozrEaXnsSL3kdTD01X+4LfIWOuFzTzuoslBrBILfHN
# j8RfOxPgjuwNvE6YzauXi4orp4Sm6tF245DaFOSYbWFK5ZgG6cUY2/bUq3g3bQAq
# Zt65KcaewEJ3ZyNEobv35Nf6xN6FrA6jF9447+NHvCjeWLCQZ3M8lgeCcnnhTFty
# QX3XgCoc6IRXvFOcPVrr3D9RPHCMS6Ckg8wggTrtIVnY8yjbvGOUsAdZbeXUIQAW
# Ms0d3cRDv09SvwVRd61evQIDAQABo4ICGzCCAhcwDgYDVR0PAQH/BAQDAgGGMBAG
# CSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRraSg6NS9IY0DPe9ivSek+2T3bITBU
# BgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoG
# CCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQF
# MAMBAf8wHwYDVR0jBBgwFoAUyH7SaoUqG8oZmAQHJ89QEE9oqKIwgYQGA1UdHwR9
# MHsweaB3oHWGc2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRp
# ZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcmwwgZQGCCsGAQUFBwEBBIGHMIGE
# MIGBBggrBgEFBQcwAoZ1aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# ZXJ0cy9NaWNyb3NvZnQlMjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUyMFJvb3Ql
# MjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAuY3J0MA0GCSqGSIb3DQEB
# DAUAA4ICAQBfiHbHfm21WhV150x4aPpO4dhEmSUVpbixNDmv6TvuIHv1xIs174bN
# GO/ilWMm+Jx5boAXrJxagRhHQtiFprSjMktTliL4sKZyt2i+SXncM23gRezzsoOi
# Bhv14YSd1Klnlkzvgs29XNjT+c8hIfPRe9rvVCMPiH7zPZcw5nNjthDQ+zD563I1
# nUJ6y59TbXWsuyUsqw7wXZoGzZwijWT5oc6GvD3HDokJY401uhnj3ubBhbkR83Rb
# fMvmzdp3he2bvIUztSOuFzRqrLfEvsPkVHYnvH1wtYyrt5vShiKheGpXa2AWpsod
# 4OJyT4/y0dggWi8g/tgbhmQlZqDUf3UqUQsZaLdIu/XSjgoZqDjamzCPJtOLi2hB
# wL+KsCh0Nbwc21f5xvPSwym0Ukr4o5sCcMUcSy6TEP7uMV8RX0eH/4JLEpGyae6K
# i8JYg5v4fsNGif1OXHJ2IWG+7zyjTDfkmQ1snFOTgyEX8qBpefQbF0fx6URrYiar
# jmBprwP6ZObwtZXJ23jK3Fg/9uqM3j0P01nzVygTppBabzxPAh/hHhhls6kwo3QL
# J6No803jUsZcd4JQxiYHHc+Q/wAMcPUnYKv/q2O444LO1+n6j01z5mggCSlRwD9f
# aBIySAcA9S8h22hIAcRQqIGEjolCK9F6nK9ZyX4lhthsGHumaABdWzCCB5cwggV/
# oAMCAQICEzMAAABV2d1pJij5+OIAAAAAAFUwDQYJKoZIhvcNAQEMBQAwYTELMAkG
# A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UE
# AxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAwHhcN
# MjUxMDIzMjA0NjQ5WhcNMjYxMDIyMjA0NjQ5WjCB2zELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdEMDAtMDVFMC1E
# OTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5n
# IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL25H5Ie
# WUiz9DAlFmn2sPymaFWbvYkMfK+ScIWb3a1IvOlIwghUDjY0Gp6yMRhfYURiGS0G
# edIB6ywvuH6VBCX3+bdOFcAclgtv21jrpOjZmk4fSaT2Q3BszUfeUJa8o3xI7Zfo
# MY9dszTxHQAz6ZVX87fHGEVhQcfxW33IdPJOj/ae419qtYxT21MVmCfsTshgtWio
# QxmOW/vMC9/b+qgtBxSMf798vm3qfmhF6KCvFaHlivrM32hY16PGE3L0PFC+LM7v
# RxU7mTb+r76CeybvqOWk4+dbKYftPhV1t/E5S/6wwXeYmu/Y7JC7Tnh2w45G5Y4p
# cM3oHMb/YuPRdOWa0v+RC2QgmNVWqjuxDiylWscXQDuaMtb29AcdGUVV9ZsRY2M2
# sthAtOdZOshiR5ufMtaHtiCkWv0jNfgUxrHurxzYuUNneWZ6EfQDgFAw8CSCKkSO
# K2c9jEop4ddVq10xvbqxdrqMneVXvvIcXrPQAXj9j2ECpV2EwMb3Wnmpw00P78Jp
# zPsk3Fs61ZvOGd/F1RcOBu6f2TWdp7HL7+rq7tgHr13MldbfIWu4lpoYYE1gTQa1
# Yrg5XN4j7zs9klT2z3qocmPzV8DWQgIHNh+aTs7bujMEMQyI7Xt1zPxZCgcR6H0t
# mmzU/9BxvsWbRalCQ2sYGyWupTdc4e7KY7kPAgMBAAGjggHLMIIBxzAdBgNVHQ4E
# FgQUVgRfEG3cCAPwyL+pyRbKwdesZbYwHwYDVR0jBBgwFoAUa2koOjUvSGNAz3vY
# r0npPtk92yEwbAYDVR0fBGUwYzBhoF+gXYZbaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0
# YW1waW5nJTIwQ0ElMjAyMDIwLmNybDB5BggrBgEFBQcBAQRtMGswaQYIKwYBBQUH
# MAKGXWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9z
# b2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAyMDIwLmNy
# dDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB
# /wQEAwIHgDBmBgNVHSAEXzBdMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wCAYGZ4EMAQQCMA0GCSqGSIb3DQEBDAUAA4ICAQBSHuGSVHvalCnFnlsq
# XIQefH1xP2SFr9g+Vz+f5P7QeywjfQb5jUlSmd1XnJUDPe/MHxL7r3TEElL+mNtG
# 6CDPAytStSFPXD9tTBtBMYh8Wqo64pH9qm361yIqeBH979mzWCkMQsTd0nM6dUl9
# B+7qiti+ToXwxIl39eYqLuYYfhD2mqqePXMzUKSQzkf73yYIVHP6nLJQz4aAmaWc
# fG9jg78sBkDV8KpW7JgktuLhphJEN1B+SVHjenPdcmrFXIUu/K4jK5ukfWaQIjua
# XzSjBlNjC5tQN6adPfA3GxUwHPeR4ekL5If/9vBf13tmzBW+gy+0sNGTveb9IL9G
# U8iX8UvywsX62nhCCPRUhTigDBKdczRUrNrntBhowbfchBDFML8avRMRc9Gmc2Jv
# IryX336SFQ51//q1UU2HMSJEMhWLJSIWJVhfUowsOa+PampIzETYfFvTu2mqKJUl
# WZXkGYxrdCvCczJcqeoadpW1ul6kcdnDh228SQ8ZhDc6IRlM4iNd5SNoNgX+aom3
# wuGyjUaSaPZWxPB1G2NKiYhPLt0lPHg0Gskj1zhISY8UQkMMDr3o2JgRuT+wnJED
# QUp55ddvhSkSoD6I9DL/s+TjIY/c9jLaW5xywJHqdKHUApRMsghv7kebSua1upmR
# +TquelFktDSOjVdSRkuya4uoxTGCB0YwggdCAgEBMHgwYTELMAkGA1UEBhMCVVMx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9z
# b2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjACEzMAAABV2d1pJij5
# +OIAAAAAAFUwDQYJYIZIAWUDBAIBBQCgggSfMBEGCyqGSIb3DQEJEAIPMQIFADAa
# BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDYx
# NTIyMTkwOVowLwYJKoZIhvcNAQkEMSIEIBsvIePVOdhXqWKoFAcIN8IJIM+y6hwP
# nhpApLacNZt/MIG5BgsqhkiG9w0BCRACLzGBqTCBpjCBozCBoAQg2Lk8l2SGYru/
# ff7+D2qrJnkswcYdK6pGKu7GGGr4/s0wfDBlpGMwYTELMAkGA1UEBhMCVVMxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjACEzMAAABV2d1pJij5+OIA
# AAAAAFUwggNhBgsqhkiG9w0BCRACEjGCA1AwggNMoYIDSDCCA0QwggIsAgEBMIIB
# CaGB4aSB3jCB2zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEl
# MCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMe
# blNoaWVsZCBUU1MgRVNOOjdEMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3Nv
# ZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eaIjCgEBMAcGBSsO
# AwIaAxUAHTtUAYJlv7bgWVeRBo4X7FeHDeqgZzBlpGMwYTELMAkGA1UEBhMCVVMx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9z
# b2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAwDQYJKoZIhvcNAQEL
# BQACBQDt2tkwMCIYDzIwMjYwNjE1MjAwNDAwWhgPMjAyNjA2MTYyMDA0MDBaMHcw
# PQYKKwYBBAGEWQoEATEvMC0wCgIFAO3a2TACAQAwCgIBAAICDhMCAf8wBwIBAAIC
# E3MwCgIFAO3cKrACAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAK
# MAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEALSPNa8g5
# q/5xz99ZnHPZjBW1k333P9pHT7+rkPFsw26HdE6nXk1xjOqlOxBCeSV8WGOsg9k5
# +wWndps1lkRYeVx6+kV56Jlxul9/S6N9M6VK2fV14opZ1KXcEIfrvjHt5C8LZF7/
# KJQlRe0bAyWEBkCwSy16IOgDx9ptPTYtG/IU6r3K5FHcKGW/sfF5sNgMNE/4xUIf
# CmvByx2LR8/FHqHX9/aIrsAuXlphBqbqK10KE395RukIUvMPFE0I9FSka1aTrXZg
# JzEAwg/16VZO9za3qjeKkkVKvc5p1CI9bIY8kBhf/gdjFRiP+k2Yiep4A6vWrAx3
# 1iBjXRTRLM5C4jANBgkqhkiG9w0BAQEFAASCAgALjB/eQGAOfcXCjW9U3S7AJ0Zg
# YQvpy1yOIjSbtH/Q5OnTNb+P2XCVK10LRNttFCmAetjebMAmoKmUYvKyVZ6e25t1
# fhGeWDpogERsLrgPFow9Z1UWMzrzDpUhhlCjYk2qVvBBTmtoW8xu5feQXrGR5dp+
# UU/ZK638kfbj+0Ct/rFNO3slZ8586Hy5oEv3y4ESKnwar1PRlZ9NfwSuJgs3b5Ed
# TFYBoWOL3VVnJPcCwZkXTGQFjtqHjttLiEQZQjLQ8HEDSy3U3xAjWy6RH7hog+L2
# ILbNywwY46iFxovLZ4oqowS6Yn9JheO8TnrOgiX/cz9R4Hwk92xVyIGjj57lGOCJ
# A/Xt/KOLpsuPqtbwdx9DveOwZ21ZnPGY1qgQxJg0PG50mhTxMXKC5QPzXhZUk1VT
# D7r9DKAcsHAgmQoTGHgVJZ9ZLmQeWkDZYpr/v1PpuAwhoTyT47ucLQwUEhOkiEgx
# zphiQsRkhyHTlaCQxdwHmhyx/Qq7c2a3pDVQGfZg8Tt374t6Y7DdSHqv3YZs/5vs
# 0dpuwxIhamGLhtlcgiN1Iytga1UGAyq33K/C4lhhrW86s026PQsrpkFuXP1Va6UW
# pJz0NwgRsOS6cY4NRycIjv2/Srw7v1tbgqjjn+t/58ERz6nnDbtKI5Bk1RdLLYFu
# F3ePdAkiUNps6kwXsQ==
# SIG # End signature block