Private/ps1/Watch-ApprxrFolder.ps1

<##
.SYNOPSIS
    Watches a folder for new incoming files and triggers an action when a new file is detected.
.DESCRIPTION
    Uses .NET FileSystemWatcher to monitor a specified folder for new files. When a new file is created, the script outputs the file name or performs a custom action.
.PARAMETER Path
    The folder path to watch for new files.
.PARAMETER Filter
    The file filter (e.g., *.txt) to watch for. Default is * (all files).
.EXAMPLE
    Watch-ApprxrFolder -Path 'C:\Logs' -Filter '*.log'
    Watches the C:\Logs folder for new .log files.
##>

function Watch-ApprxrFolder {
    param(
        [Parameter(Mandatory)]
        [string]$Path,
        [string]$Filter = '*',
        [string]$InProgressFolder,
        [string]$VDBName,
        [string]$JobName,
        [string]$LocationName,
        [string]$StateFilePath
    )

    function Get-PositiveIntFromConfig {
        param(
            [Parameter(Mandatory)]
            [string]$Name,
            [Parameter(Mandatory)]
            [int]$Default
        )

        $raw = $null
        try {
            $raw = @(Get-ApprxrConfigurationValue -name $Name -defaultValue $Default)[0]
        } catch {
            return $Default
        }

        $parsed = 0
        if ([int]::TryParse([string]$raw, [ref]$parsed) -and $parsed -gt 0) {
            return $parsed
        }

        return $Default
    }

    # -- Internal helper: wait until a file exists AND is fully released ---
    # Returns $true when ready, $false when the timeout expires.
    function Wait-FileReady {
        param(
            [string]$FilePath,
            [int]$MaxWaitSeconds    = 30,
            [int]$RetryIntervalMs   = 500
        )
        $deadline = (Get-Date).AddSeconds($MaxWaitSeconds)
        $attempt  = 0

        while ((Get-Date) -lt $deadline) {
            $attempt++

            # Step 1 - does the file exist at all yet?
            if (-not (Test-Path -LiteralPath $FilePath)) {
                Log "[FileWatcher] Wait attempt $attempt - '$([System.IO.Path]::GetFileName($FilePath))' not present yet, retrying in ${RetryIntervalMs}ms..."
                Start-Sleep -Milliseconds $RetryIntervalMs
                continue
            }

            # Step 2 - try to open the file exclusively to confirm no other
            # process still holds a write lock (e.g. mid-copy).
            $lockStream = $null
            try {
                $lockStream = [System.IO.File]::Open(
                    $FilePath,
                    [System.IO.FileMode]::Open,
                    [System.IO.FileAccess]::ReadWrite,
                    [System.IO.FileShare]::None
                )
                return $true   # file is present and fully released
            } catch [System.IO.IOException] {
                Log "[FileWatcher] Wait attempt $attempt - '$([System.IO.Path]::GetFileName($FilePath))' is still locked, retrying in ${RetryIntervalMs}ms..."
                Start-Sleep -Milliseconds $RetryIntervalMs
            } catch {
                # Unexpected error - surface it to the caller
                throw
            } finally {
                # Always release the exclusive lock, even if an unexpected error occurred
                if ($null -ne $lockStream) {
                    try { $lockStream.Close()   } catch {}
                    try { $lockStream.Dispose() } catch {}
                }
            }
        }
        return $false  # timed out
    }

    # -- Internal helper: persist watcher state to a JSON file ----
    function Update-WatcherState {
        param([hashtable]$WatcherState, [string]$FilePath)
        if (-not $FilePath) { return }
        try {
            $WatcherState.LastHeartbeat = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
            $dir = Split-Path $FilePath -Parent
            if (-not (Test-Path $dir)) {
                New-Item -ItemType Directory -Path $dir -Force | Out-Null
            }
            $json = $WatcherState | ConvertTo-Json -Depth 5
            $stateStream = $null
            $stateWriter = $null
            try {
                $stateStream = [System.IO.File]::Open(
                    $FilePath,
                    [System.IO.FileMode]::Create,
                    [System.IO.FileAccess]::Write,
                    [System.IO.FileShare]::ReadWrite
                )
                $stateWriter = [System.IO.StreamWriter]::new($stateStream, [System.Text.Encoding]::UTF8)
                $stateWriter.Write($json)
                $stateWriter.Flush()
            } finally {
                if ($null -ne $stateWriter) { try { $stateWriter.Close()   } catch {}; try { $stateWriter.Dispose() } catch {} }
                if ($null -ne $stateStream) { try { $stateStream.Close()   } catch {}; try { $stateStream.Dispose() } catch {} }
            }
        } catch {
            # Never let a state-write failure break the watcher
        }
    }

    # -- Internal helper: process a single file (wait-until-ready, VDB upload, InProgress move) --
    # Returns a fileEntry tracking hashtable. Pass -IsFallback to prefix log lines with [Fallback].
    function Invoke-ProcessFile {
        param(
            [string]$FilePath,
            [string]$FileName,
            [switch]$IsFallback
        )

        $tag = if ($IsFallback) { ' [Fallback]' } else { '' }

        $fileEntry = [ordered]@{
            FileName   = $FileName
            FilePath   = $FilePath
            DetectedAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
            SizeBytes  = $null
            VdbStatus  = 'Pending'
            MoveStatus = 'N/A'
            Error      = $null
        }

        Log "[FileWatcher][$label][$instanceId]$tag Waiting for '$FileName' to be ready..."
        $isReady = Wait-FileReady -FilePath $FilePath -MaxWaitSeconds 30 -RetryIntervalMs 500

        if ($isReady) {
            try {
                $fileInfo = Get-Item -LiteralPath $FilePath -ErrorAction Stop
                $fileEntry.SizeBytes = $fileInfo.Length
                Log "[FileWatcher][$label][$instanceId]$tag File ready: Name='$($fileInfo.Name)' Size=$($fileInfo.Length) bytes Extension='$($fileInfo.Extension)' Created='$($fileInfo.CreationTime)'"

                Log "[FileWatcher][$label][$instanceId]$tag Reading file bytes for VDB upload..."
                $fileBytes     = [System.IO.File]::ReadAllBytes($FilePath)
                $base64Encoded = [Convert]::ToBase64String($fileBytes)
                $fileBytes     = $null  # release raw bytes - no longer needed

                $body = @{
                    innerObject = @{
                        File          = $base64Encoded
                        FileName      = $fileInfo.Name
                        FileExtension = $fileInfo.Extension
                        FileSize      = $fileInfo.Length
                        State         = 'new'
                        CreationDate  = $fileInfo.CreationTime.ToString('yyyy-MM-ddTHH:mm:ss')
                        SendDate      = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
                    }
                    key          = $fileInfo.Name
                    databaseName = $VDBName
                    ttl          = 7
                } | ConvertTo-Json -Depth 5
                $base64Encoded = $null  # release base64 string after JSON serialisation

                Log "[FileWatcher][$label][$instanceId]$tag Sending '$($fileInfo.Name)' ($($fileInfo.Length) bytes) to VDB '$VDBName'..."
                $result = Get-ApprxrResult -request 'api/services/app/VDB/SaveToVirtualDb' -body $body
                $body   = $null  # release serialised body

                if ($result -and $result.success -eq $true) {
                    Log "[FileWatcher][$label][$instanceId]$tag VDB upload SUCCESS: '$($fileInfo.Name)' -> VDB='$VDBName'"
                    $fileEntry.VdbStatus = 'Success'
                } else {
                    $errDetail = if ($result) { ($result | ConvertTo-Json -Compress -Depth 3) } else { '(null response)' }
                    Log "[FileWatcher][$label][$instanceId]$tag VDB upload FAILED: '$($fileInfo.Name)' | Response: $errDetail"
                    $fileEntry.VdbStatus      = "Failed: $errDetail"
                    $watcherState.LastError   = "VDB upload failed for '$FileName': $errDetail"
                    $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
                }
            } catch {
                $errMsg = "$($_.Exception.Message) | Stack: $($_.ScriptStackTrace)"
                Log "[FileWatcher][$label][$instanceId]$tag ERROR processing file '$FileName': $errMsg"
                $fileEntry.VdbStatus      = 'Error'
                $fileEntry.Error          = $errMsg
                $watcherState.LastError   = "Error processing '$FileName': $($_.Exception.Message)"
                $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
            }
        } else {
            Log "[FileWatcher][$label][$instanceId]$tag TIMEOUT: '$FileName' did not become ready within 30 s. Skipping."
            $fileEntry.VdbStatus      = 'Timeout'
            $fileEntry.Error          = 'File did not become ready within 30 seconds (still locked or never appeared)'
            $watcherState.LastError   = "Timeout waiting for '$FileName'"
            $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        }

        # Move to InProgress folder (only when file is still present)
        if ($InProgressFolder -and (Test-Path -LiteralPath $FilePath)) {
            $destPath = Join-Path $InProgressFolder ([System.IO.Path]::GetFileName($FilePath))
            Log "[FileWatcher][$label][$instanceId]$tag Moving '$FileName' -> '$destPath'"
            try {
                Move-Item -Path $FilePath -Destination $destPath -Force -ErrorAction Stop
                Log "[FileWatcher][$label][$instanceId]$tag Move SUCCESS: '$FileName' -> '$destPath'"
                $fileEntry.MoveStatus = 'Moved'
            } catch {
                $errMsg = $_.Exception.Message
                Log "[FileWatcher][$label][$instanceId]$tag Move FAILED: '$FileName' -> '$destPath' | Error: $errMsg"
                $fileEntry.MoveStatus     = "Failed: $errMsg"
                $watcherState.LastError   = "Move failed for '$FileName': $errMsg"
                $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
            }
        }

        [System.GC]::Collect()
        return $fileEntry
    }

    # -- Internal helper: fallback scan --
    # Runs after every watcher-triggered file (and when skip.txt is detected).
    # Scans $Path for any files matching $Filter that are older than 5 minutes
    # and processes them via Invoke-ProcessFile so files dropped while the
    # service was down are not permanently missed.
    # skip.txt is always deleted regardless of $Filter; the user can drop it
    # to force an immediate fallback sweep without waiting for a real file.
    function Invoke-FallbackScan {
        param([switch]$IgnoreAge)

        $ageHint = if ($IgnoreAge) { 'all matching files' } else { 'files older than 5 min' }
        Log "[FileWatcher][$label][$instanceId] [Fallback] Scanning '$Path' for missed files ($ageHint)..."
        try {
            # Always clean up skip.txt regardless of the active filter.
            $skipTxtPath = Join-Path $Path 'skip.txt'
            if (Test-Path -LiteralPath $skipTxtPath) {
                try {
                    Remove-Item -LiteralPath $skipTxtPath -Force -ErrorAction Stop
                    Log "[FileWatcher][$label][$instanceId] [Fallback] Removed stale 'skip.txt'"
                } catch {
                    Log "[FileWatcher][$label][$instanceId] [Fallback] WARNING: Could not remove 'skip.txt': $($_.Exception.Message)"
                }
            }

            $candidates = Get-ChildItem -LiteralPath $Path -Filter $Filter -File -ErrorAction SilentlyContinue |
                Where-Object { $_.Name -ine 'skip.txt' }

            if (-not $candidates) {
                Log "[FileWatcher][$label][$instanceId] [Fallback] No files match filter '$Filter' in '$Path'."
                return
            }

            $found = 0
            foreach ($candidate in $candidates) {
                $age = (Get-Date) - $candidate.LastWriteTime
                if ((-not $IgnoreAge) -and $age.TotalMinutes -lt 5) { continue }

                $found++
                Log "[FileWatcher][$label][$instanceId] [Fallback] Missed file: '$($candidate.Name)' Age=$([math]::Round($age.TotalMinutes, 1)) min - processing..."
                $fileEntry = Invoke-ProcessFile -FilePath $candidate.FullName -FileName $candidate.Name -IsFallback
                $watcherState.DocumentsProcessed++
                $watcherState.LastFiles = @(@($watcherState.LastFiles) + @($fileEntry)) | Select-Object -Last 10
                Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
                Log "[FileWatcher][$label][$instanceId] [Fallback] Done: '$($candidate.Name)'. TotalProcessed=$($watcherState.DocumentsProcessed)"
            }

            if ($found -eq 0) {
                Log "[FileWatcher][$label][$instanceId] [Fallback] No missed files found."
            }
        } catch {
            Log "[FileWatcher][$label][$instanceId] [Fallback] ERROR during scan: $($_.Exception.Message)"
        }
    }

    $label      = if ($LocationName) { $LocationName } else { 'Unknown' }
    $instanceId = if ($JobName)      { $JobName }      else { "PID$PID" }
    $fallbackRetrySeconds = Get-PositiveIntFromConfig -Name 'FileWatcherFallbackRetrySeconds' -Default 360
    $internalBufferSize   = Get-PositiveIntFromConfig -Name 'FileWatcherInternalBufferSizeBytes' -Default 32768
    if ($internalBufferSize -lt 4096) { $internalBufferSize = 4096 }
    if ($internalBufferSize -gt 65536) { $internalBufferSize = 65536 }

    # Initialise in-memory state (written to file on every significant event)
    $watcherState = [ordered]@{
        JobName            = $instanceId
        LocationName       = $label
        WatchPath          = $Path
        Filter             = $Filter
        Pid                = $PID
        StartedAt          = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        LastHeartbeat      = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        DocumentsProcessed = 0
        LastError          = $null
        LastErrorAt        = $null
        LastFiles          = @()
    }
    Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath

    # -- Validate watch path before starting ----
    if (-not (Test-Path $Path)) {
        Log "[FileWatcher][$label][$instanceId] STARTUP ERROR: Watch path does not exist: '$Path'"
        $watcherState.LastError   = "Watch path does not exist: $Path"
        $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
        return
    }

    if ($InProgressFolder -and -not (Test-Path $InProgressFolder)) {
        Log "[FileWatcher][$label][$instanceId] Creating InProgress folder: '$InProgressFolder'"
        try {
            New-Item -ItemType Directory -Path $InProgressFolder -Force | Out-Null
        } catch {
            Log "[FileWatcher][$label][$instanceId] WARNING: Could not create InProgress folder '$InProgressFolder': $_"
        }
    }

    # -- Create and configure FileSystemWatcher ----
    # Wrapped in try-catch so a failure here (e.g. Log-Apprxr cannot write
    # to the log file in a service account context) is recorded in the state
    # file rather than silently killing the job.
    $watcher = $null
    $watcherSignals = [hashtable]::Synchronized(@{ Overflow = $false })
    $watcherErrorSubscription = $null
    try {
        $watcher = New-Object System.IO.FileSystemWatcher
        $watcher.Path = $Path
        $watcher.Filter = $Filter
        $watcher.InternalBufferSize = $internalBufferSize
        $watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite, CreationTime, Size'
        $watcher.EnableRaisingEvents = $true
        $watcher.IncludeSubdirectories = $false

        $watcherErrorSubscription = Register-ObjectEvent -InputObject $watcher -EventName Error -MessageData $watcherSignals -Action {
            $state = $Event.MessageData
            if ($null -ne $state) {
                $state.Overflow = $true
            }
        }
    } catch {
        $errMsg = "$($_.Exception.Message) | Stack: $($_.ScriptStackTrace)"
        Log "[FileWatcher][$label][$instanceId] FATAL: Could not create FileSystemWatcher for '$Path': $errMsg"
        $watcherState.LastError   = "FileSystemWatcher setup failed: $errMsg"
        $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
        return
    }

    try { Log "[FileWatcher][$label][$instanceId] STARTED. Path='$Path' Filter='$Filter' InProgress='$InProgressFolder' VDB='$VDBName' PID=$PID FallbackRetrySeconds=$fallbackRetrySeconds InternalBufferSize=$internalBufferSize" } catch {}

    $heartbeatTick  = 0
    $heartbeatEvery = 30   # log a heartbeat every ~30 seconds (1 s poll timeout per tick)

    $fallbackStopwatch = [System.Diagnostics.Stopwatch]::StartNew()

    try {
        while ($true) {
            $changeTypes = [System.IO.WatcherChangeTypes]::Created -bor [System.IO.WatcherChangeTypes]::Renamed
            $change = $watcher.WaitForChanged($changeTypes, 1000)

            if ($watcherSignals.Overflow) {
                Log "[FileWatcher][$label][$instanceId] WARNING: FileSystemWatcher buffer overflow detected. Triggering immediate fallback sweep."
                Invoke-FallbackScan -IgnoreAge
                $watcherSignals.Overflow = $false
                $fallbackStopwatch.Restart()
                $heartbeatTick = 0
                Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
            }

            if ($fallbackStopwatch.Elapsed.TotalSeconds -ge $fallbackRetrySeconds) {
                Log "[FileWatcher][$label][$instanceId] Periodic fallback trigger reached (${fallbackRetrySeconds}s)."
                Invoke-FallbackScan
                $fallbackStopwatch.Restart()
                $heartbeatTick = 0
                Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
            }

            # Periodic heartbeat so we know the loop is still alive
            $heartbeatTick++
            if ($heartbeatTick -ge $heartbeatEvery) {
                $heartbeatTick = 0
                Log "[FileWatcher][$label][$instanceId] Heartbeat - Path='$Path' Filter='$Filter' Processed=$($watcherState.DocumentsProcessed) PID=$PID"
                Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
            }

            if ($change.TimedOut) { continue }

            $filePath = Join-Path $Path $change.Name
            Log "[FileWatcher][$label][$instanceId] File detected: '$($change.Name)'"

            # skip.txt: delete immediately and trigger a fallback sweep.
            # The user can drop this file into the watched folder to force
            # processing of any files missed while the service was down.
            # Note: this branch only fires when the active $Filter matches *.txt
            # or *. Invoke-FallbackScan also cleans up skip.txt independently.
            if ($change.Name -ieq 'skip.txt') {
                if (Test-Path -LiteralPath $filePath) {
                    try {
                        Remove-Item -LiteralPath $filePath -Force -ErrorAction Stop
                        Log "[FileWatcher][$label][$instanceId] Trigger file 'skip.txt' deleted - starting fallback scan..."
                    } catch {
                        Log "[FileWatcher][$label][$instanceId] WARNING: Could not delete 'skip.txt': $($_.Exception.Message)"
                    }
                }
                Invoke-FallbackScan
                $heartbeatTick = 0
                Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
                continue
            }

            # Process the file: wait until ready, upload to VDB, move to InProgress.
            $fileEntry = Invoke-ProcessFile -FilePath $filePath -FileName $change.Name

            # Update running state
            $watcherState.DocumentsProcessed++
            $watcherState.LastFiles = @(@($watcherState.LastFiles) + @($fileEntry)) | Select-Object -Last 10
            $heartbeatTick = 0
            Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
            Log "[FileWatcher][$label][$instanceId] Done processing '$($change.Name)'. TotalProcessed=$($watcherState.DocumentsProcessed)"

            # Fallback scan: after every watcher-triggered file, check whether
            # any files were dropped while the service was down (older than 5 min).
            Invoke-FallbackScan
            $fallbackStopwatch.Restart()
        }
    } catch {
        $errMsg = "$($_.Exception.Message) | Stack: $($_.ScriptStackTrace)"
        Log "[FileWatcher][$label][$instanceId] FATAL ERROR in watcher loop: $errMsg"
        $watcherState.LastError   = "Fatal error: $($_.Exception.Message)"
        $watcherState.LastErrorAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
    } finally {
        try { Log "[FileWatcher][$label][$instanceId] Disposing watcher. Path='$Path' TotalProcessed=$($watcherState.DocumentsProcessed)" } catch {}
        if ($null -ne $watcherErrorSubscription) {
            try { Unregister-Event -SubscriptionId $watcherErrorSubscription.Id -ErrorAction SilentlyContinue } catch {}
            try { Remove-Job -Id $watcherErrorSubscription.Id -Force -ErrorAction SilentlyContinue } catch {}
        }
        if ($null -ne $watcher) { try { $watcher.Dispose() } catch {} }
        Update-WatcherState -WatcherState $watcherState -FilePath $StateFilePath
    }
}

# SIG # Begin signature block
# MIInKgYJKoZIhvcNAQcCoIInGzCCJxcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDzYJC/anvi051Q
# 6WBd/UClcWqMBWmC/ut4HjdyJ7cF9KCCIKYwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggaXMIIEf6ADAgECAhAfsijAKebq6quDMIHCRvIfMA0GCSqG
# SIb3DQEBCwUAMGoxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT
# eXN0ZW1zIFMuQS4xODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9u
# IENvZGUgU2lnbmluZyAyMDIxIENBMB4XDTI2MDIwMzEzNTE0NFoXDTI4MDIwMzEz
# NTE0M1owgaExHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
# BAGCNzwCAQMTAk5MMREwDwYDVQQFEwg5NjMyNDU2MjELMAkGA1UEBhMCTkwxEDAO
# BgNVBAgMB1V0cmVjaHQxEzARBgNVBAcMCkFtZXJzZm9vcnQxETAPBgNVBAoMCEFQ
# UFJYIEJWMREwDwYDVQQDDAhBUFBSWCBCVjCCAaIwDQYJKoZIhvcNAQEBBQADggGP
# ADCCAYoCggGBAOF+mHloTjmkknba8mOejw0Gq3Aczy1dtatfdLM70ken0n07zIlY
# 2jNmyTv5I5RixXAt3LJUwWnUQH8okGFJYvnLZRPyjpCxnEqq8GL6puhKjIIcTs8W
# VhoADX+mFtLXygLrfk1j6A+dnNXjoCQMsVSEYppSGrpMpKsPQONb1mmFRx66vqro
# HcP93qWYTm/f3TpRzlscIxcQ/3Ab+KLeW0sHgC/a8MVHLicbdUe/pka4jMM98Djj
# BtkBc18dI6ECtrDfB2FXXFomvgm5UyPaf2sYxMPL+X/iCNhaQevoT3vttxMvKzAD
# JxJK7VsV2nrCvxfTaP4cVd9FfoxUqjCE0eA/IH3qrKqAv3eONjMrRzoO9jJumusi
# t91wYT1dwTe/o2FAwfmZ7qxofCXTlO008weUDY5UBw8d+BavVvnDNR08eEcCJN37
# wBJn1hhjFZZD6OxVcMEoyhIG5tJqvmBPyR5A1oYeGSL+/xBbLpAQR0oAhAivxvn4
# POX2r4M/wXGJewIDAQABo4IBfzCCAXswDAYDVR0TAQH/BAIwADBBBgNVHR8EOjA4
# MDagNKAyhjBodHRwOi8vY2V2Y3NjYTIwMjEuY3JsLmNlcnR1bS5wbC9jZXZjc2Nh
# MjAyMS5jcmwwdwYIKwYBBQUHAQEEazBpMC4GCCsGAQUFBzABhiJodHRwOi8vY2V2
# Y3NjYTIwMjEub2NzcC1jZXJ0dW0uY29tMDcGCCsGAQUFBzAChitodHRwOi8vcmVw
# b3NpdG9yeS5jZXJ0dW0ucGwvY2V2Y3NjYTIwMjEuY2VyMB8GA1UdIwQYMBaAFKxX
# yggW3D/FMRwKTdv78d6ZJy00MB0GA1UdDgQWBBSEZ5pbrzKazBz6ZCRYAbuQVlDy
# pDBKBgNVHSAEQzBBMAcGBWeBDAEDMDYGCyqEaAGG9ncCBQEHMCcwJQYIKwYBBQUH
# AgEWGWh0dHBzOi8vd3d3LmNlcnR1bS5wbC9DUFMwEwYDVR0lBAwwCgYIKwYBBQUH
# AwMwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQARtX2dHRUVP9uH
# 5FuVIvj22UsNMW2gsexnRxeL/xKxY4BXv6UkQmcgABKbYlWGH0wodaKRhrT9NyK1
# tt0P1C53zAkaiwNU7gSge7s/OPk2yA7g++iVBUDXRelZVCWi31V8yRVV+zXAFEkK
# timx00QcEhLFu1DAyjABmKouChHJyqF4dHWytvQZQHw1dfG1OSh6F82thRipW/q1
# ggWzm91QdieGlaSBZy9N881+h4V23R7oJVyWcCUtar19rBJH+/TaZHMo3VDVazhr
# CYUKMf/f6RA+iuSsPcpwMi7BZzTxawQtEe/1vG3RLVP94bWl69gY8LiTe0OU23tp
# 1PuTiHFLEXjTIb9WnBljH2PvUpJVaxQAMZrunKZvuafYSAvcEfSiRvgFg3r1UH9I
# nLjJVxsjfxjnbg5y/5SChh+OBbld7+NdvWVoFUGwqK2IuALD0O2/gtQBS/+UZuiB
# GsWpTv9PWI9/C2kPjEcBjC3sZedZOjkEJNSb8siUDItoswA+ddZRBBLdkNJCIKX4
# +lrKAAXdOqGu1QSu57G/OOLyjeUHEWx73XzoPMdgGfkcbdSBQoWBevDbC2xLzqr6
# EQZhplxGR39HZ4M4GyNFcpL4Xhuw1Gma8ldGTeuNw5ktP5Jmf9zvrjP5epHgMl6r
# jO0VG4wGU+R4e/FPQClKsyxI/cTJMjCCBrQwggScoAMCAQICEA3HrFcF/yGZLkBD
# Igw6SYYwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY
# RGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTI1MDUwNzAwMDAwMFoXDTM4MDEx
# NDIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0
# MDk2IFNIQTI1NiAyMDI1IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBALR4MdMKmEFyvjxGwBysddujRmh0tFEXnU2tjQ2UtZmWgyxU7UNqEY81FzJs
# Qqr5G7A6c+Gh/qm8Xi4aPCOo2N8S9SLrC6Kbltqn7SWCWgzbNfiR+2fkHUiljNOq
# nIVD/gG3SYDEAd4dg2dDGpeZGKe+42DFUF0mR/vtLa4+gKPsYfwEu7EEbkC9+0F2
# w4QJLVSTEG8yAR2CQWIM1iI5PHg62IVwxKSpO0XaF9DPfNBKS7Zazch8NF5vp7ea
# Z2CVNxpqumzTCNSOxm+SAWSuIr21Qomb+zzQWKhxKTVVgtmUPAW35xUUFREmDrMx
# SNlr/NsJyUXzdtFUUt4aS4CEeIY8y9IaaGBpPNXKFifinT7zL2gdFpBP9qh8SdLn
# Eut/GcalNeJQ55IuwnKCgs+nrpuQNfVmUB5KlCX3ZA4x5HHKS+rqBvKWxdCyQEEG
# cbLe1b8Aw4wJkhU1JrPsFfxW1gaou30yZ46t4Y9F20HHfIY4/6vHespYMQmUiote
# 8ladjS/nJ0+k6MvqzfpzPDOy5y6gqztiT96Fv/9bH7mQyogxG9QEPHrPV6/7umw0
# 52AkyiLA6tQbZl1KhBtTasySkuJDpsZGKdlsjg4u70EwgWbVRSX1Wd4+zoFpp4Ra
# +MlKM2baoD6x0VR4RjSpWM8o5a6D8bpfm4CLKczsG7ZrIGNTAgMBAAGjggFdMIIB
# WTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTvb1NK6eQGfHrK4pBW9i/U
# SezLTjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8E
# BAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQu
# Y3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsG
# CWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAF877FoAc/gc9EXZxML2+C8i1
# NKZ/zdCHxYgaMH9Pw5tcBnPw6O6FTGNpoV2V4wzSUGvI9NAzaoQk97frPBtIj+ZL
# zdp+yXdhOP4hCFATuNT+ReOPK0mCefSG+tXqGpYZ3essBS3q8nL2UwM+NMvEuBd/
# 2vmdYxDCvwzJv2sRUoKEfJ+nN57mQfQXwcAEGCvRR2qKtntujB71WPYAgwPyWLKu
# 6RnaID/B0ba2H3LUiwDRAXx1Neq9ydOal95CHfmTnM4I+ZI2rVQfjXQA1WSjjf4J
# 2a7jLzWGNqNX+DF0SQzHU0pTi4dBwp9nEC8EAqoxW6q17r0z0noDjs6+BFo+z7bK
# SBwZXTRNivYuve3L2oiKNqetRHdqfMTCW/NmKLJ9M+MtucVGyOxiDf06VXxyKkOi
# rv6o02OoXN4bFzK0vlNMsvhlqgF2puE6FndlENSmE+9JGYxOGLS/D284NHNboDGc
# mWXfwXRy4kbu4QFhOm0xJuF2EZAOk5eCkhSxZON3rGlHqhpB/8MluDezooIs8CVn
# rpHMiD2wL40mm53+/j7tFaxYKIqL0Q4ssd8xHZnIn/7GELH3IdvG2XlM9q7WP/Uw
# gOkw/HQtyRN62JK4S1C8uw3PdBunvAZapsiI5YKdvlarEvf8EA+8hcpSM9LHJmyr
# xaFtoza2zNaQ9k+5t1wwggbNMIIEtaADAgECAhEAu/DMtbe4Mf0hrjJ3iuQMiTAN
# BgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8g
# VGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9u
# IEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAy
# MB4XDTIxMDUxOTA1MzIxM1oXDTM2MDUxODA1MzIxM1owajELMAkGA1UEBhMCUEwx
# ITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjE4MDYGA1UEAxMvQ2Vy
# dHVtIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCUvvjwlqjf9eedRTK+NoavZlVx
# mQTPIN1nK5XQRUQWyPWwHD54wa2aD2kRDrAjBf71/T0XN9Oqms5ML/Jolgryv3JW
# 7/yahp7lg/wn3PDnNwzC1BlFMyCdEktQmUXMmE4mmxSutRY1OGGagENWPjbQ1R/x
# 4HuTByWbG+TUV7dIPxE7TMy9Npco4f09DN6QFRXdk7bZTu58kxu6MFH6mzQkC3IX
# vExfo0c/CT4/hTZD2XknHjXLzi9jzInSHS9GQqeXd6nWbkwEu7znKpNEcSElorDS
# EcRhc1OSOLjUK0x0cOw8whzQ0fWGkPoqgCslbjZoko7U7kj/Fn4TWQ8s75LHjhVa
# o+w2SWMP0ELWYIkq1ComGq3wqBD7VU6rtCDoUf237HkgRBxLXFjwp444oKWnchYM
# Z7epWX8jqntSr4QHeidL72c6XswQIIZsFglV4BO28jO02yS7w4iyY0c6PQs34GZ7
# SWphsN1DaiX9ysc6hovVYoVA94t+3p4TI1RtK9Ayh0atmfYPJqtBfk5kJ1h4nNax
# 0lZmJL9chJjZQb+7XW8MQgstiQdsegaz2QL6Edm+94N8IW6TYaalyO9LeR35A9Mo
# TLo79rdB04naTqWdPTBKh7DpS53ZAQn5dYs2per+m7ZeIxnEWaNcG+PbRh+ZslFb
# 6M75JgN43szB+/VYVQIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
# HQ4EFgQUrFfKCBbcP8UxHApN2/vx3pknLTQwHwYDVR0jBBgwFoAUtqFUOQLDoD+O
# irz61PgcptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5j
# cmwwbAYIKwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2Nz
# cC1jZXJ0dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0
# dW0ucGwvY3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcC
# ARYYaHR0cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC7
# khlZjFbU+gsRdcraFFJKj1PL63MVudnm/AILuNaxm7LIZOYtrZaRWEpR0qd3uhSp
# UEI29fMBZvuu3CHXJsM58hbJFo4YoHnyDJgTkeVmc10fd1NI//cI3nRD1/Alx09y
# WhPZlze0A/Bvwc3LcZtTywfsL5tD3QAL8ZqelrHt1Eb8AnDEnAzKNbm1mwYmD/5d
# y1XkIKfsM1pmRM2ZDcJ4qpwVewE7g+4e+Y+iq42LuaHPvNAkwq0daA9Hp3qemk0c
# RFfM5257MvEsu+6w8+44mpQUOgpASVIjY3l2vdf6BUfBJfsd3pcp/2hfDnXaql9A
# oB7ps7eCZb9VXKm/u2lTRMf5esOcZFy4udsnGBaOwSSUe0chYusfJ9IjsmpoMl4C
# W5E0R/U55Bz6TpDo+gaXQJL4i/zv0UR5papGsDguBASp9g/OfnvGTeWrS9l5orEk
# tu9ExrFfPvAKxZK+4AO9bapPhMATraKo6kx6K51pS1ZO2pf1r3B+lXN5bTLgp8g/
# dygUZm2cmauBAzJA5v/iHOb0b4b4Muqc8dxgcX1jZRLtLvxnAWxkKIMfj7A4M0Jr
# N4rLBUpPFM0+Bp2sR7rtFqNZW56huaDkxO75eNRN2z4A959R5GBRnFWtgE65+B1R
# HAQQ/TOF4mU0Y3oIhVQr3rUh4tdSvIxr8QpsDKIvsDCCBu0wggTVoAMCAQICEAqA
# 7xhLjfEFgtHEdqeVdGgwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVk
# IEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2
# MDQwMDAwMDBaFw0zNjA5MDMyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQw
# OTYgVGltZXN0YW1wIFJlc3BvbmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQDQRqwtEsae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ
# 3xTWcfsLwOvRxUwXcGx8AUjni6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqV
# Q+3bzWYesFtkepErvUSbf+EIYLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjo
# T1FpS54dNApZfKY61HAldytxNM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R
# 0V3Zp3DjjANwqAf4lEkTlCDQ0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6Un
# bksIcFJqLbkIXIPbcNmA98Oskkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39
# iTTFS+ENTqW8m6THuOmHHjQNC3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0
# dVVZw7knh1WZXOLHgDvundrAtuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6ll
# N3QgshRta6Eq4B40h5avMcpi54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wg
# gn8O2klETsJ7u8xEehGifgJYi+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmS
# F3voIgMFtNGh86w3ISHNm0IaadCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwID
# AQABo4IBlTCCAZEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85Fx
# YxlQQ89hjOgwHwYDVR0jBBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0P
# AQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSB
# iDCBhTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsG
# AQUFBzAChlFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0f
# BFgwVjBUoFKgUIZOaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1Ud
# IAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEA
# ZSqt8RwnBLmuYEHs0QhEnmNAciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZ
# hY+hIfP2JkQ38U+wtJPBVBajYfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/
# ciZmUnthfAEP1HShTrY+2DE5qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk
# 6FxRPyUPxAAYH2Vy1lNM4kzekd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQ
# USntbjZ80FU3i54tpx5F/0Kr15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdC
# G1+lqvP4FbrQ6IwSBXkZagHLhFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y5
# 8678IgmfORBPC1JKkYaEt2OdDh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V0
# 8X1JUPvB4ILfJdmL+66Gp3CSBXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1q
# mcwbdUfcSYCn+OwncVUXf53VJUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfY
# xJ7La54i71McVWRP66bW+yERNpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhD
# Bf3Froguzzhk++ami+r3Qrx5bIbY3TVzgiFI7Gq3zWcxggXaMIIF1gIBATB+MGox
# CzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4x
# ODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9uIENvZGUgU2lnbmlu
# ZyAyMDIxIENBAhAfsijAKebq6quDMIHCRvIfMA0GCWCGSAFlAwQCAQUAoIGEMBgG
# CisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIE
# IA8UAAt4IgR9y3fI3KzJlxB2V4qajBeNQjzu6FWy8AbgMA0GCSqGSIb3DQEBAQUA
# BIIBgIV4O5xAkQekIfW/sLwj9hKG1iSnbftLHvEA4tuDNeXy56w7/w245v8i5hVD
# R0/wgfpe0OM0y8aDPo4/bGp91jJJ2PC4ilhxeHnAbWkPD+ikhbsQor2oJT5p32Ya
# fi5adn6HMjlhgKcp3iM54Xnw+k86nuF4V++4NLBTkntyZRA8kjKVOGND+qm6noMh
# /F4qVRvUORAF0B+5WyPMZoplpG8xSVV0JW61A0wbQ0aPOXOwIlk3eel3SFUuBzs6
# dT3oST6Iirr5FE/QsiNAt6r1Dmhp5VP08cviMeEoT/kRGn9P+26YX2ldAU1ym22Q
# 0ktljDfz+Ohoa7SI18mp9Ss6FLwaa8BLstRNSnCY2roEsZ+aLBM+usluCCwEiHK1
# r1JlwY8pUhkFmYZRLpSXXmJmfDL9TW6h4wBY6i8WcnTIb3S6SraAofF0R3pWo1vB
# 0KwHVv13S2Ct0mqZzxbxORDmfm6nQCveiPb/lXblB1QMMlpwXPHexhiCJK3xXNgn
# 6FNybKGCAyYwggMiBgkqhkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYT
# AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQg
# VHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEC
# EAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMx
# CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjA0MDkwODIxMTNaMC8GCSqG
# SIb3DQEJBDEiBCDB1BYBq5yS0l4Z8RFq01TJxnj9rFAo4GlG5LF3xIThhzANBgkq
# hkiG9w0BAQEFAASCAgCeoC5Gjp6e9PNsFVgboMUadOT1GLvBq/4prJZNOCnURqQb
# pRdfhwT7TPQnKjswZbEMIabsqmSkoOaTfqCngMpdS67/vL/TkHBO6k/del7k7/h7
# ePFvYBki/+gmHTlGj9U7RfMOly1rVR7OOCrb7PZhqkZeTNGKEJalmu7AErlyShAZ
# YU2g2gRqNivOZK1MKbcZuENCymYIGWrgk3fd7oWxYc21TP2+35P6NGVffHw72cBt
# MR1gB6VeCRHq/xeteH68+FYDJPRi61fVObyEz1EHBmJIjyCLjQuZZxRBPh6KP6Yt
# fB+/MkzP95Co3wByNCqUrExsRLVBRTxrjwvQ+pYiVLLqTipzc98ci9BjIzYH4sGk
# DhtYwZARb5YArvmsshDmfbD/IKPxcC41/Z2xVkHE3SC1H6NeL2Z98CRFM56BZvok
# dNkkMtIi0UQ9MjBDkL3Yo3xPztXTGLFlc3K3qdEhFKLlNkO4sEtyGprDyFmCBJvk
# LNjRmz49i/ENnVhAsPra2uDky7s+OmW7XdNZ7a2NtQ+qXhHLGX2ZPNZt3G6hSEln
# fqiO5gbk+SRYnBqOlfXMPsQpogaujpK3vCTZwaDDnDVf7X0NnW+j0Eb7Dy/bB3qs
# 36qeanrUDGk7Moq6wZsBZ3ap9FWrOXoIiRZsdevAb5YBxAXD2DuZjiPBxeCUsw==
# SIG # End signature block