Private/Invoke-EntraOpsEAMClassificationAggregation.ps1

<#
.SYNOPSIS
    Aggregates classifications per unique object and builds standardized EAM output objects.

.DESCRIPTION
    Shared helper that replaces the duplicated classification aggregation logic found in every
    EAM cmdlet's final stage. Handles both parallel and sequential processing paths, builds
    unique classification sets per object, applies "Unclassified" fallback, and constructs
    the standardized output PSCustomObject via New-EntraOpsEAMOutputObject.

    Principals whose object details could not be resolved ($null entry in ObjectDetailsCache) are
    NOT dropped: they are emitted fail-closed as ControlPlane placeholders with a Classification
    marker entry (TaggedBy = "UnresolvedObject"), so downstream Administrative Unit / Conditional
    Access group reconciliation never removes a potentially privileged principal from its
    protection group just because a Graph lookup failed or was throttled.

    This function eliminates ~150 lines of duplicated code per EAM cmdlet.

.PARAMETER UniqueObjects
    Array of unique objects with ObjectId and ObjectType properties.

.PARAMETER ObjectDetailsCache
    Hashtable mapping ObjectId → resolved object details from Get-EntraOpsPrivilegedEntraObject.

.PARAMETER RbacClassificationsByObject
    Hashtable mapping ObjectId → classified role assignments (from Group-Object ObjectId -AsHashTable).

.PARAMETER RoleSystem
    The RBAC system name (e.g., "EntraID", "Defender", "DeviceManagement", "IdentityGovernance", "ResourceApps").

.PARAMETER EnableParallelProcessing
    Enable parallel processing. Default is $true.

.PARAMETER ParallelThrottleLimit
    Maximum number of parallel threads. Default is 10.

.PARAMETER WarningMessages
    Reference to the List[psobject] for collecting warnings.

.OUTPUTS
    Array of standardized EAM output objects.
#>


function Merge-EntraOpsEAMClassification {
    <#
    .SYNOPSIS
        Merges the Classification entries of all role assignments of one object into the object-level
        classification set (one entry per AdminTierLevel / AdminTierLevelName / Service).

    .DESCRIPTION
        Entries sharing the same (case-insensitive) AdminTierLevel|AdminTierLevelName|Service key are
        merged instead of keeping only the first one:
        - MatchedActions: case-insensitive union across all contributing entries, sorted (OrdinalIgnoreCase);
          for casing variants of the same action the ordinal-smallest spelling is kept. $null if no entry
          carries actions. Always an array otherwise (also for a single action).
        - ScopedObjects: union de-duplicated by identity (id / ObjectId / string value / JSON), sorted by
          identity. $null if no entry carries scoped objects.
        - AdminTierLevel / AdminTierLevelName / Service: ordinal-smallest spelling (deterministic label for
          casing variants such as "Default member" vs. "Default Member").
        - Other properties (e.g. Justification): ordinal-smallest non-null value.
        TaggedBy* properties are excluded. The result is independent of the role assignment order and
        sorted by AdminTierLevel, AdminTierLevelName, Service. Pure CPU helper without module dependencies.

    .PARAMETER RoleAssignments
        Role assignments (each with a Classification array) contributing to one object.

    .OUTPUTS
        Merged classification entries (no output if no classification exists); wrap the call in @().
    #>

    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [AllowEmptyCollection()]
        [object[]]$RoleAssignments
    )

    $ExcludedProperties = [System.Collections.Generic.HashSet[string]]::new(
        [string[]]@('TaggedBy', 'TaggedByObjectIds', 'TaggedByObjectDisplayNames', 'TaggedByRoleSystem'),
        [System.StringComparer]::OrdinalIgnoreCase)

    $Groups = [System.Collections.Generic.Dictionary[string, System.Collections.Generic.List[object]]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($Assignment in @($RoleAssignments)) {
        if ($null -eq $Assignment -or $null -eq $Assignment.Classification) { continue }
        foreach ($ClassItem in @($Assignment.Classification)) {
            if ($null -eq $ClassItem) { continue }
            $Key = "$($ClassItem.AdminTierLevel)|$($ClassItem.AdminTierLevelName)|$($ClassItem.Service)"
            if (-not $Groups.ContainsKey($Key)) {
                $Groups[$Key] = [System.Collections.Generic.List[object]]::new()
            }
            $Groups[$Key].Add($ClassItem)
        }
    }

    $Merged = foreach ($Items in $Groups.Values) {
        $PropertyNames = [System.Collections.Generic.List[string]]::new()
        foreach ($Item in $Items) {
            foreach ($Property in $Item.PSObject.Properties) {
                if (-not $ExcludedProperties.Contains($Property.Name) -and -not $PropertyNames.Contains($Property.Name)) {
                    $PropertyNames.Add($Property.Name)
                }
            }
        }

        $Entry = [ordered]@{}
        foreach ($PropertyName in $PropertyNames) {
            $Values = @(foreach ($Item in $Items) {
                    $Property = $Item.PSObject.Properties[$PropertyName]
                    if ($null -ne $Property -and $null -ne $Property.Value) { , $Property.Value }
                })

            if ($Values.Count -eq 0) {
                $Entry[$PropertyName] = $null
                continue
            }

            switch ($PropertyName) {
                'MatchedActions' {
                    $Actions = [System.Collections.Generic.Dictionary[string, string]]::new([System.StringComparer]::OrdinalIgnoreCase)
                    foreach ($Value in $Values) {
                        foreach ($Action in @($Value)) {
                            # Flatten one nesting level (e.g. ", @($MatchedActions)" wrapped arrays)
                            foreach ($ActionItem in @(if ($Action -is [System.Collections.IEnumerable] -and $Action -isnot [string]) { $Action } else { , $Action })) {
                                if ($null -eq $ActionItem) { continue }
                                $ActionString = "$ActionItem"
                                if ([string]::IsNullOrWhiteSpace($ActionString)) { continue }
                                $Existing = $null
                                if (-not $Actions.TryGetValue($ActionString, [ref]$Existing) -or [string]::CompareOrdinal($ActionString, $Existing) -lt 0) {
                                    $Actions[$ActionString] = $ActionString
                                }
                            }
                        }
                    }
                    $ActionList = [System.Collections.Generic.List[string]]::new($Actions.Values)
                    $ActionList.Sort([System.StringComparer]::OrdinalIgnoreCase)
                    $Entry[$PropertyName] = [object[]]@($ActionList)
                }
                'ScopedObjects' {
                    $Scoped = [System.Collections.Generic.Dictionary[string, object]]::new([System.StringComparer]::OrdinalIgnoreCase)
                    foreach ($Value in $Values) {
                        foreach ($ScopedObject in @($Value)) {
                            if ($null -eq $ScopedObject) { continue }
                            $IdentityKey = if ($ScopedObject -is [string]) {
                                $ScopedObject
                            } elseif ($null -ne $ScopedObject.PSObject.Properties['id'] -and -not [string]::IsNullOrEmpty("$($ScopedObject.id)")) {
                                "$($ScopedObject.id)"
                            } elseif ($null -ne $ScopedObject.PSObject.Properties['ObjectId'] -and -not [string]::IsNullOrEmpty("$($ScopedObject.ObjectId)")) {
                                "$($ScopedObject.ObjectId)"
                            } else {
                                $ScopedObject | ConvertTo-Json -Compress -Depth 5
                            }
                            $Existing = $null
                            if (-not $Scoped.TryGetValue($IdentityKey, [ref]$Existing)) {
                                $Scoped[$IdentityKey] = $ScopedObject
                            } elseif ([string]::CompareOrdinal(($ScopedObject | ConvertTo-Json -Compress -Depth 5), ($Existing | ConvertTo-Json -Compress -Depth 5)) -lt 0) {
                                $Scoped[$IdentityKey] = $ScopedObject
                            }
                        }
                    }
                    if ($Scoped.Count -eq 0) {
                        $Entry[$PropertyName] = $null
                    } else {
                        $ScopedKeys = [System.Collections.Generic.List[string]]::new($Scoped.Keys)
                        $ScopedKeys.Sort([System.StringComparer]::OrdinalIgnoreCase)
                        $Entry[$PropertyName] = [object[]]@(foreach ($ScopedKey in $ScopedKeys) { $Scoped[$ScopedKey] })
                    }
                }
                default {
                    # Deterministic pick independent of assignment order: ordinal-smallest representation
                    $Selected = $Values[0]
                    foreach ($Value in $Values) {
                        if ([string]::CompareOrdinal("$Value", "$Selected") -lt 0) { $Selected = $Value }
                    }
                    $Entry[$PropertyName] = $Selected
                }
            }
        }
        [PSCustomObject]$Entry
    }

    $Merged | Sort-Object AdminTierLevel, AdminTierLevelName, Service
}

function Invoke-EntraOpsEAMClassificationAggregation {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Array]$UniqueObjects,

        [Parameter(Mandatory = $true)]
        [hashtable]$ObjectDetailsCache,

        [Parameter(Mandatory = $true)]
        [hashtable]$RbacClassificationsByObject,

        [Parameter(Mandatory = $true)]
        [string]$RoleSystem,

        [Parameter(Mandatory = $false)]
        [bool]$EnableParallelProcessing = $true,

        [Parameter(Mandatory = $false)]
        [int]$ParallelThrottleLimit = 10,

        [Parameter(Mandatory = $false)]
        [System.Collections.Generic.List[psobject]]$WarningMessages
    )

    # Determine if parallel processing is viable (PowerShell 7+ guaranteed by module prerequisite)
    $HasSufficientObjects = $UniqueObjects.Count -ge 50
    $UseParallelForClassification = $EnableParallelProcessing -and $HasSufficientObjects

    # Fail-closed placeholder SCHEMA, defined once and shared by the parallel and sequential paths
    # so the two can never drift apart. Shared as plain data (read-only template + marker) rather
    # than a scriptblock: invoking a $using: scriptblock concurrently across parallel runspaces is
    # not thread-safe, and [scriptblock]::Create is avoided for the Constrained Language Mode goals
    # tracked in PawCompatible.md. Each path clones the template and fills in the three
    # object-specific fields (ObjectTenantId, ObjectType, ObjectDisplayName).
    $UnresolvedObjectDetailsTemplate = [ordered]@{
        'ObjectTenantId'                = $null
        'ObjectType'                    = 'unresolved'
        'ObjectSubType'                 = 'Unresolved'
        'ObjectDisplayName'             = $null
        'ObjectSignInName'              = $null
        'AdminTierLevel'                = '0'
        'AdminTierLevelName'            = 'ControlPlane'
        'OnPremSynchronized'            = $null
        'AssignedAdministrativeUnits'   = $null
        'RestrictedManagementByRAG'     = $null
        'RestrictedManagementByAadRole' = $null
        'RestrictedManagementByRMAU'    = $null
        'Sponsors'                      = $null
        'Owners'                        = $null
        'OwnedObjects'                  = $null
        'OwnedDevices'                  = $null
        'IdentityParent'                = $null
        'AssociatedWorkAccount'         = $null
        'AssociatedPawDevice'           = $null
    }
    $UnresolvedMarkerClassification = [PSCustomObject]@{
        'AdminTierLevel'     = "0"
        'AdminTierLevelName' = "ControlPlane"
        'Service'            = "Unresolved"
        'TaggedBy'           = "UnresolvedObject"
    }

    if ($UseParallelForClassification) {
        $SyncObjectDetailsCache = [System.Collections.Hashtable]::Synchronized($ObjectDetailsCache)
        $SyncRbacByObject = [System.Collections.Hashtable]::Synchronized($RbacClassificationsByObject)

        # Merge classifications up front: the parallel runspaces cannot call module functions, and
        # the merge is CPU-only and cheap compared to the per-object output construction.
        $MergedClassificationByObject = @{}
        foreach ($UniqueObject in $UniqueObjects) {
            if ($null -ne $UniqueObject.ObjectId -and -not $MergedClassificationByObject.ContainsKey($UniqueObject.ObjectId)) {
                $MergedClassificationByObject[$UniqueObject.ObjectId] = @(Merge-EntraOpsEAMClassification -RoleAssignments @($RbacClassificationsByObject[$UniqueObject.ObjectId]))
            }
        }
        $SyncMergedClassificationByObject = [System.Collections.Hashtable]::Synchronized($MergedClassificationByObject)

        $ClassificationThrottleLimit = [Math]::Min($ParallelThrottleLimit * 2, 100)
        Write-Host "Using parallel classification processing with $ClassificationThrottleLimit threads for $($UniqueObjects.Count) objects..." -ForegroundColor Yellow

        $ClassifiedObjects = $UniqueObjects | ForEach-Object -ThrottleLimit $ClassificationThrottleLimit -Parallel {
            $obj = $_
            $ObjectId = $obj.ObjectId

            if ($null -ne $ObjectId) {
                $SharedDetailsCache = $using:SyncObjectDetailsCache
                $SharedRbacByObject = $using:SyncRbacByObject
                $LocalRoleSystem = $using:RoleSystem

                $ObjectDetails = $SharedDetailsCache[$ObjectId]

                # Fail-closed: never drop a principal whose object details could not be resolved
                # (failed/throttled Graph call: $null cache entry or ResolutionStatus 'Unresolved'). A vanished ControlPlane principal
                # would otherwise be removed from its protection group by the Administrative Unit /
                # Conditional Access group reconciliation. Emit a placeholder pinned to ControlPlane
                # instead, built inline from the identity fields known from the assignment data
                # (cannot call module functions from parallel runspace).
                $IsUnresolvedObject = ($null -eq $ObjectDetails -or "$($ObjectDetails.ResolutionStatus)" -eq 'Unresolved')
                if ($IsUnresolvedObject) {
                    # Shared schema template (see its definition in function scope) - clone and
                    # fill the object-specific fields.
                    $DetailsTemplate = $using:UnresolvedObjectDetailsTemplate
                    $Details = [ordered]@{}
                    foreach ($TemplateKey in $DetailsTemplate.Keys) { $Details[$TemplateKey] = $DetailsTemplate[$TemplateKey] }
                    $Details['ObjectTenantId'] = $obj.ObjectTenantId
                    $KnownObjectType = "$($obj.ObjectType)"
                    if (-not [string]::IsNullOrWhiteSpace($KnownObjectType)) { $Details['ObjectType'] = $KnownObjectType }
                    $Details['ObjectDisplayName'] = $ObjectId
                    $ObjectDetails = [PSCustomObject]$Details
                }

                $RbacClassifiedAssignments = $SharedRbacByObject[$ObjectId]

                # Merged classifications are precomputed in the calling runspace (module helpers are not
                # available here); see Merge-EntraOpsEAMClassification.
                $SharedMergedClassification = $using:SyncMergedClassificationByObject
                $Classification = @($SharedMergedClassification[$ObjectId])
                if ($Classification.Count -eq 0) {
                    $Classification = @([PSCustomObject]@{
                            'AdminTierLevel'     = "Unclassified"
                            'AdminTierLevelName' = "Unclassified"
                            'Service'            = "Unclassified"
                        })
                }

                if ($IsUnresolvedObject) {
                    # Marker entry flags the fail-closed placeholder in the export and also serves as
                    # the structured warning marker folded into $WarningMessages after the parallel
                    # block. Keep any classifications derived from the assignments themselves, but
                    # drop the generic "Unclassified" fallback in favor of the explicit marker.
                    $Classification = @(($using:UnresolvedMarkerClassification).PSObject.Copy()) + @($Classification | Where-Object { $_.AdminTierLevelName -ne "Unclassified" })
                }

                # Build output object inline (cannot call module functions from parallel runspace).
                [PSCustomObject]@{
                    'ObjectId'                      = $ObjectId
                    'ObjectTenantId'                = $ObjectDetails.ObjectTenantId
                    'ObjectType'                    = ($ObjectDetails.ObjectType ?? 'unknown').ToLower()
                    'ObjectSubType'                 = $ObjectDetails.ObjectSubType
                    'ObjectDisplayName'             = $ObjectDetails.ObjectDisplayName
                    'ObjectUserPrincipalName'       = $ObjectDetails.ObjectSignInName
                    'ObjectAdminTierLevel'          = $ObjectDetails.AdminTierLevel
                    'ObjectAdminTierLevelName'      = $ObjectDetails.AdminTierLevelName
                    'OnPremSynchronized'            = $ObjectDetails.OnPremSynchronized
                    'AssignedAdministrativeUnits'   = $ObjectDetails.AssignedAdministrativeUnits
                    'RestrictedManagementByRAG'     = $ObjectDetails.RestrictedManagementByRAG
                    'RestrictedManagementByAadRole' = $ObjectDetails.RestrictedManagementByAadRole
                    'RestrictedManagementByRMAU'    = $ObjectDetails.RestrictedManagementByRMAU
                    'RoleSystem'                    = $LocalRoleSystem
                    'Classification'                = $Classification
                    'RoleAssignments'               = @($RbacClassifiedAssignments | Sort-Object { ($_.Classification | Sort-Object AdminTierLevel -Top 1).AdminTierLevel }, RoleDefinitionName, RoleAssignmentScopeId, RoleAssignmentId)
                    'Sponsors'                      = $ObjectDetails.Sponsors
                    'Owners'                        = $ObjectDetails.Owners
                    'OwnedObjects'                  = $ObjectDetails.OwnedObjects
                    'OwnedDevices'                  = $ObjectDetails.OwnedDevices
                    'IdentityParent'                = $ObjectDetails.IdentityParent
                    'AssociatedWorkAccount'         = $ObjectDetails.AssociatedWorkAccount
                    'AssociatedPawDevice'           = $ObjectDetails.AssociatedPawDevice
                }
            }
        }

        # Fold unresolved-object markers from the parallel results into the warning collection:
        # parallel runspaces cannot append to $WarningMessages directly, so each placeholder carries
        # its marker (Classification TaggedBy = "UnresolvedObject") and is surfaced here.
        $UnresolvedClassifiedObjects = @($ClassifiedObjects | Where-Object { @($_.Classification).TaggedBy -contains "UnresolvedObject" })
        foreach ($UnresolvedObject in $UnresolvedClassifiedObjects) {
            $UnresolvedWarning = "Object $($UnresolvedObject.ObjectId) could not be resolved - emitted as fail-closed ControlPlane placeholder (TaggedBy=UnresolvedObject)"
            if ($null -ne $WarningMessages) {
                $WarningMessages.Add([PSCustomObject]@{
                        Type    = "UnresolvedObject"
                        Message = $UnresolvedWarning
                        Target  = $UnresolvedObject.ObjectId
                    })
            }
            Write-Warning $UnresolvedWarning
        }

        if ($ClassifiedObjects.Count -ne $UniqueObjects.Count -and $null -ne $WarningMessages) {
            $WarningMessages.Add([PSCustomObject]@{Type = "Stage-Classification-Parallel"; Message = "Parallel classification returned fewer objects than expected. Expected: $($UniqueObjects.Count), Actual: $($ClassifiedObjects.Count)" })
            Write-Warning "Parallel classification returned fewer objects than expected. Expected: $($UniqueObjects.Count), Actual: $($ClassifiedObjects.Count)"
        }
    } else {
        if ($EnableParallelProcessing) {
            Write-Host "Using sequential classification processing (dataset too small: $($UniqueObjects.Count) objects)" -ForegroundColor Yellow
        } else {
            Write-Host "Using sequential classification processing (parallel disabled)..." -ForegroundColor Yellow
        }

        $ClassifiedObjects = $UniqueObjects | ForEach-Object {
            if ($null -ne $_.ObjectId) {
                $ObjectId = $_.ObjectId
                if ($VerbosePreference -ne 'SilentlyContinue') {
                    Write-Verbose -Message "Processing classifications for $($ObjectId)..."
                }

                # Object types
                $ObjectDetails = $ObjectDetailsCache[$ObjectId]

                # Fail-closed: never drop a principal whose object details could not be resolved
                # (failed/throttled Graph call: $null cache entry or ResolutionStatus 'Unresolved'). A vanished ControlPlane principal
                # would otherwise be removed from its protection group by the Administrative Unit /
                # Conditional Access group reconciliation. Emit a placeholder pinned to ControlPlane
                # instead, built from the identity fields known from the assignment data.
                $IsUnresolvedObject = ($null -eq $ObjectDetails -or "$($ObjectDetails.ResolutionStatus)" -eq 'Unresolved')
                if ($IsUnresolvedObject) {
                    $UnresolvedWarning = "Object $ObjectId could not be resolved - emitted as fail-closed ControlPlane placeholder (TaggedBy=UnresolvedObject)"
                    if ($null -ne $WarningMessages) {
                        $WarningMessages.Add([PSCustomObject]@{
                                Type    = "UnresolvedObject"
                                Message = $UnresolvedWarning
                                Target  = $ObjectId
                            })
                    }
                    Write-Warning $UnresolvedWarning

                    # Shared schema template (see its definition in function scope) - clone and
                    # fill the object-specific fields.
                    $Details = [ordered]@{}
                    foreach ($TemplateKey in $UnresolvedObjectDetailsTemplate.Keys) { $Details[$TemplateKey] = $UnresolvedObjectDetailsTemplate[$TemplateKey] }
                    $Details['ObjectTenantId'] = $_.ObjectTenantId
                    $KnownObjectType = "$($_.ObjectType)"
                    if (-not [string]::IsNullOrWhiteSpace($KnownObjectType)) { $Details['ObjectType'] = $KnownObjectType }
                    $Details['ObjectDisplayName'] = $ObjectId
                    $ObjectDetails = [PSCustomObject]$Details
                }

                # RBAC Assignments
                $RbacClassifiedAssignments = $RbacClassificationsByObject[$ObjectId]

                # Classification - merge MatchedActions/ScopedObjects across all assignments per tier/service
                $Classification = @(Merge-EntraOpsEAMClassification -RoleAssignments @($RbacClassifiedAssignments))
                if ($Classification.Count -eq 0) {
                    $Classification = @([PSCustomObject]@{
                            'AdminTierLevel'     = "Unclassified"
                            'AdminTierLevelName' = "Unclassified"
                            'Service'            = "Unclassified"
                        })
                }

                if ($IsUnresolvedObject) {
                    # Marker entry flags the fail-closed placeholder in the export. Keep any
                    # classifications derived from the assignments themselves, but drop the generic
                    # "Unclassified" fallback in favor of the explicit marker.
                    $Classification = @($UnresolvedMarkerClassification.PSObject.Copy()) + @($Classification | Where-Object { $_.AdminTierLevelName -ne "Unclassified" })
                }

                New-EntraOpsEAMOutputObject `
                    -ObjectId $ObjectId `
                    -ObjectDetails $ObjectDetails `
                    -Classification $Classification `
                    -RoleAssignments @($RbacClassifiedAssignments) `
                    -RoleSystem $RoleSystem
            }
        }
    }

    return $ClassifiedObjects
}

# SIG # Begin signature block
# MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBIlP+sQczzdHuf
# e6ABs82ulKz5cBL145OgGMQcl9GlTaCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j
# AEkoya49fu0nMA0GCSqGSIb3DQEBDAUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
# ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy
# dGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5l
# dHdvcmsgQ0EwHhcNMjEwNTMxMDY0MzA2WhcNMjkwOTE3MDY0MzA2WjCBgDELMAkG
# A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAl
# BgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMb
# Q2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAvfl4+ObVgAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC
# 5SBLm9qbe7mZXdmbgEvXhEArJ9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Va
# qp3cKcniNQfrcE1K1sGzVrihQTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ
# 0WeNG0a+RzDVLnLRxWPa52N5RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet
# /5oCl8HGUJKbAiy9qbk0WQq/hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNe
# GWJoRVfkkIJCu0LW8GHgwaM9ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcV
# WiNoidQ+3k4nsPBADLxNF8tNorMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbl
# s+V1e9dBkQXcXWnjlQ1DufyDljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZb
# MI28iQzV13D4h1L92u+sUS4Hs07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+Nqc
# noYsylfzGuXIkosagpZ6w7xQEmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbl
# jnX98sy50IdbzAYQYLuDNbdeZ95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEA
# AaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLahVDkCw6A/joq8
# +tT4HKbROg79MB8GA1UdIwQYMBaAFAh2zcsH/yT2xc3tu5C84oQ3RnX3MA4GA1Ud
# DwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vY3JsLmNlcnR1bS5w
# bC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBdMCgGCCsGAQUFBzABhhxodHRwOi8v
# c3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsGAQUFBzAChiVodHRwOi8vcmVwb3Np
# dG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQG
# CCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQEM
# BQADggEBAFHCoVgWIhCL/IYx1MIy01z4S6Ivaj5N+KsIHu3V6PrnCA3st8YeDrJ1
# BXqxC/rXdGoABh+kzqrya33YEcARCNQOTWHFOqj6seHjmOriY/1B9ZN9DbxdkjuR
# mmW60F9MvkyNaAMQFtXx0ASKhTP5N+dbLiZpQjy6zbzUeulNndrnQ/tjUoCFBMQl
# lVXwfqefAcVbKPjgzoZwpic7Ofs4LphTZSJ1Ldf23SIikZbr3WjtP6MZl9M7JYjs
# NhI9qX7OAo0FmpKnJ25FspxihjcNpDOO16hO0EoXQ0zF8ads0h5YbBRRfopUofbv
# n3l6XYGaFpAP4bvxSgD5+d2+7arszgowggZrMIIEU6ADAgECAhBX5gfP8LabvnxC
# XoItEBa/MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNjEwMDYwODI4MjNaFw0yNzEwMDYwODI4MjJaMIGQ
# MQswCQYDVQQGEwJERTEdMBsGA1UECAwUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQ
# BgNVBAcMCUxhaG5zdGVpbjEeMBwGA1UECgwVT3BlbiBTb3VyY2UgRGV2ZWxvcGVy
# MS4wLAYDVQQDDCVPcGVuIFNvdXJjZSBEZXZlbG9wZXIgVGhvbWFzIE5hdW5oZWlt
# MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAphp2ljSmVsRB6gvkEq7A
# DUMENpKZiDFoAfF1ktD1nCwaVyI+bJ0ROjHwAn4iJgS+vlBDPOUIw2GLHkiqUhOg
# HSwzg6gj45GNmt6zxsKP0zPlhMB/fb0zmz7/F7e3yfKCXLfhbHuk0Es9gJ+8Wmet
# 9StVwwSE/K0MpW06GKA9nPWyE4N/ADwigDIj9rsTKJR4cve9w8G1b3zohpUc/qMr
# onab+HVw5r9b3sOBnnaXC6zvrX4fovRz1ztwfWjRjILLoFG6mW/TuK5F2ICD3Rhl
# 9R2rJWWWhzTDMsQlzvX+/SaM74q3OFpIVNdhvtmWU9pKUBRAyYMijcDqKEFGSzrc
# 8JFmGMpP5TuroWF+N21d3px2eARvW1mV0DCfFAH+Og20SH/gVW6/ltQQEkH/z/Zt
# 49ARyJrgESLOQtlMCIYSoKISdkk4ml4VUS1E/PcbzXEYj3a7m6DOxcd3WuY03UT9
# cnk3p0LMN6LeLYAZgVY+HRloZlH+kW2SkOHNuWgtXvZhAgMBAAGjggF4MIIBdDAM
# BgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3NjYTIwMjEu
# Y3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcwZTAsBggr
# BgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20wNQYIKwYB
# BQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIwMjEuY2Vy
# MB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQWBBTBvGyH
# MXj1dMbVnxuof9oP4PY7cDBLBgNVHSAERDBCMAgGBmeBDAEEATA2BgsqhGgBhvZ3
# AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwvQ1BTMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEAkC165c7/r6y3VeEEpNXas9bQs6ZpKPdJffHkf9xoPi4kWvAIWGc4ocG1
# DJLb18RXiDYShDmT5sb/6I08FeipKi2DNJkuOq6yOTJ3S7+hNFIZCyd6i7WKO8ea
# P5I+FAbyJP4+5coZoMg45bUa9JoocaJCm+5n7WiCfN7/EQpClW5Trm4+ZMbp7EMN
# F1uLJgGCMnN0SsoUarp7PHlqupMxMC4Uhhe+lAP2ytcSIU81L97/qjrADbLWqICk
# 9il5iGM+aZndguq/cmfH9rJj4GRZHWnG6WwnzYD9+KgYlRI6em+/9dq+IRiXe2Bs
# thUuIaFW7U2gWw8tBTEYm1UBLHdJx+L1gjJCLs5KT/wy5wU32wZxNDqPUQejUC1x
# GvU3cKqmLgRq+yNf3gFVfzqIoggyM3U9wQvnk7nBtCkpjXS2hdx4yxzcmrlWsDHr
# z6+6/jeyQySzbYUlFmKnc4n9Mw2soHOajEua5poFARVRS5KgUBuiiQ21YFNYzWBo
# qGuECaaAI/C5rGxapMJ3dAOBnHw6NLqBet+ZXBJEccX5y89MLlOQu+jDmJP8HieR
# qDLlwfhVZnw4RY8Rus43NZBwhNlVuOUXqocHKmJhKNlekda3qsJj93eQBQOxKJcc
# w5O08q9jukpH2+yzFAj+7ll33DuNUgWptogc4Awtbx3glzcEvJQwggaCMIIEaqAD
# AgECAhAo8HfBHDa9/l90MkdwJy4DMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYT
# AlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMT
# G0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQTAeFw0yNjAzMTEwNzM0NTRaFw0z
# NjAyMjcwNzM0NTRaMFAxCzAJBgNVBAYTAlBMMSEwHwYDVQQKDBhBc3NlY28gRGF0
# YSBTeXN0ZW1zIFMuQS4xHjAcBgNVBAMMFUNlcnR1bSBUaW1lc3RhbXAgMjAyNjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALi9zfBw+xI12hi2F9uNQBNO
# STkTIi13fuh3DiWybI5392bBfJw6tO7zDs7UqP/rejVf4xFTU3XNOPsuOFkP5SPF
# snWwn3VJL+NhNhsuujvYsQtaQnvNo1wk46reTgws1L8OFEdKaxgyqT4zOQx81mLw
# NZSqXNU9Rrb9oAJWJF4Mydz+C1ebu0D0+vgM3tqAQ8TU203fqgKCPqIdHloaFqH9
# eyR+8tnpmy2hnnN59ZOtmsuHeRwf5iTOhlhf0d/qqquLOSIGPgGVL1Hj5XBUox2O
# E7tQRr+l/xxNbL6OuwBO4Aa2EbuxYg37cpXv5i2/lTL4NASB/duf9W4fjA3Ro+GP
# +bC54n2OYiM3Cq8z92ejx8F5HWO2e4/75eC95B9tMw/eq7109GDyW2+2ut5Bn0o8
# MXJTERkyystWbqcnUkomA97qfiDCwvpWuOMdfTOksXe9S+xDxOJaCbiyTivgPulx
# cT+tfQTRXBr5UwtPvc+INnafOEilzVHA11tv+pHcylryxBc08cUihf/ttqkZS0bm
# x3VxQay8uohaTp0//ECA9vYGW51SKdJMJhXvKyIdBLy8WYNchTt8yRITv9Adxg9J
# qGujh+TnP1bW3aQjFtlEl7SzgNeI8OVA/CJAWWUuRT3Au9ToOpHy/flrrknhN4he
# Ew7/6ILMv4HSsj0YZ4otAgMBAAGjggFQMIIBTDB1BggrBgEFBQcBAQRpMGcwOwYI
# KwYBBQUHMAKGL2h0dHA6Ly9zdWJjYS5yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdHNj
# YTIwMjEuY2VyMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMB8GA1UdIwQYMBaAFL5UAi+/QGxzQ86sCSVOnkNEGu7gMAwGA1UdEwEB/wQC
# MAAwOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL3N1YmNhLmNybC5jZXJ0dW0ucGwv
# Y3RzY2EyMDIxLmNybDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E
# BAMCB4AwIgYDVR0gBBswGTAIBgZngQwBBAIwDQYLKoRoAYb2dwIFAQswHQYDVR0O
# BBYEFCM5aiirmhKnpKfyH/lFmr+N/oIDMA0GCSqGSIb3DQEBDAUAA4ICAQBm/pOb
# rAveTqYFPCSthxNVwBHTW4TfuBQY2KGCdMlH6ALjVPYf2XARNGMTXScR0JQ2/c2L
# xLpoHHhRwxFf3d933DhFS165rYj6iPSQKm41rc9AMpTCjMdmai1aK9o870pOzTzL
# RhWFVIxm+5Qf+t4V5z14tPoUsAWdfRKD1Zzvzfe5AHlE9rxOZcGROiULxs3Edf7Z
# J8RZI0jzD9juj2oEX2XS7BVKuNCK1W0h1mjapv028yqo8UtZh5/1Ib48Yyp88BDY
# Shh5PSGUUuS5tYXVfcduIPBe3PdNTYRYdT5VNxkwLgjMzIcr/6SH2fejFJDjnCxj
# vXLuarErJg/IdI+hXdGIBDVOGv1zrkF4ktoQvFZIp5DIAYh0GwyUhc87qqn5UaYT
# j2+iyiBDSNh7aGCrkuAnqg7Qh7frhX+QhMGeJ9SpLpnG76qN85hLyjvYR6gRYiLT
# j8G4fxjqV400/buPw0rBo9yJZyq/8cGdCOLPOYf9qpefTcrZKeO9pg+UJHx8UABb
# aTqizRxm8sUSEycccCCFhIit3AI/3wGuSOADeiA676NcQ7+dsP04YvzvdN7pTZNy
# g672WpBDJdg0eItHl4yomvNeIOWh1qs4QqOW6TI8Ocsi4uSDTLorxLUDnBTwKQzz
# pxd1YUctEgRh32R0J9FkFkeEruJU94RljFr1uDCCBrkwggShoAMCAQICEQCZo4AK
# JlU7ZavcboSms+o5MA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAG
# A1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVt
# IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3Rl
# ZCBOZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjE4WhcNMzYwNTE4MDUzMjE4WjBW
# MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu
# MSQwIgYDVQQDExtDZXJ0dW0gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQCdI88EMCM7wUYs5zNzPmNdenW6vlxNur3r
# Lfi+5OZ+U3iZIB+AspO+CC/bj+taJUbMbFP1gQBJUzDUCPx7BNLgid1TyztVLn52
# NKgxxu8gpyTr6EjWyGzKU/gnIu+bHAse1LCitX3CaOE13rbuHbtrxF2tPU8f253Q
# gX6eO8yTbGps1Mg+yda3DcTsOYOhSYNCJiL+5wnjZ9weoGRtvFgMHtJg6i671OPX
# IciiHO4Lwo2p9xh/tnj+JmCQEn5QU0NxzrOiRna4kjFaA9ZcwSaG7WAxeC/xoZSx
# F1oK1UPZtKVt+yrsGKqWONoK6f5EmBOAVEK2y4ATDSkb34UD7JA32f+Rm0wsr5aj
# zftDhA5mBipVZDjHpwzv8bTKzCDUSUuUmPo1govD0RwFcTtMXcfJtm1i+P2UNXad
# PyYVKRxKQATHN3imsfBiNRdN5kiVVeqP55piqgxOkyt+HkwIA4gbmSc3hD8ke66t
# 9MjlcNg73rZZlrLHsAIV/nJ0mmgSjBI/TthoGJDydekOQ2tQD2Dup/+sKQptalDl
# ui59SerVSJg8gAeV7N/ia4mrGoiez+SqV3olVfxyLFt3o/OQOnBmjhKUANoKLYlK
# mUpKEFI0PfoT8Q1W/y6s9LTI6ekbi0igEbFUIBE8KDUGfIwnisEkBw5KcBZ3XwnH
# mfznwlKo8QIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
# 3XRdTADbe5+gdMqxbvc8wDLAcM0wHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61Pgc
# ptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDAGA1Ud
# HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYI
# KwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0
# dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv
# Y3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0
# cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQB1iFgP5Y9Q
# KJpTnxDsQ/z0O23JmoZifZdEOEmQvo/79PQg9nLF/GJe6ZiUBEyDBHMtFRK0mXj3
# Qv3gL0sYXe+PPMfwmreJHvgFGWQ7XwnfMh2YIpBrkvJnjwh8gIlNlUl4KENTK5DL
# qsYPEtRQCw7R6p4s2EtWyDDr/M58iY2UBEqfUU/ujR9NuPyKk0bEcEi62JGxauFY
# zZ/yld13fHaZskIoq2XazjaD0pQkcQiIueL0HKiohS6XgZuUtCKA7S6CHttZEsOb
# QJ1j2s0urIDdqF7xaXFVaTHKtAuMfwi0jXtF3JJphrJfc+FFILgCbX/uYBPBlbBI
# P4Ht4xxk2GmfzMn7oxPITpigQFJFWuzTMUUgdRHTxaTSKRJ/6Uh7ki/pFjf9sUAS
# WgxT69QF9Ki4JF5nBIujxZ2sOU9e1HSCJwOfK07t5nnzbs1LbHuAIGJsRJiQ6HX/
# DW1XFOlXY1rc9HufFhWU+7Uk+hFkJsfzqBz3pRO+5aI6u5abI4Qws4YaeJH7H7M8
# X/YNoaArZbV4Ql+jarKsE0+8XvC4DJB+IVcvC9Ydqahi09mjQse4fxfef0L7E3hh
# o2O3bLDM6v60rIRUCi2fJT2/IRU5ohgyTch4GuYWefSBsp5NPJh4QRTP9DC3gc5Q
# EKtbrTY0Ka87Web7/zScvLmvQBm8JDFpDjCCBrkwggShoAMCAQICEQDn/2nHOzXO
# S5Em2HR8aKWHMA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UE
# ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
# cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBO
# ZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjA3WhcNMzYwNTE4MDUzMjA3WjBWMQsw
# CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQw
# IgYDVQQDExtDZXJ0dW0gVGltZXN0YW1waW5nIDIwMjEgQ0EwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQDpEh8ENe25XXrFppVBvoplf0530W0lddNmjtv4
# YSh/f7eDQKFaIqc7tHj7ox+u8vIsJZlroakUeMS3i3T8aJRC+eQs4FF0GqvkM6+W
# ZO8kmzZfxmZaBYmMLs8FktgFYCzywmXeQ1fEExflee2OpbHVk665eXRHjH7MYZIz
# Nnjl2m8Hy8ulB9mR8wL/W0v0pjKNT6G0sfrx1kk+3OGosFUb7yWNnVkWKU4qSxLv
# 16kJ6oVJ4BSbZ4xMak6JLeB8szrK9vwGDpvGDnKCUMYL3NuviwH1x4gZG0JAXU3x
# 2pOAz91JWKJSAmRy/l0s0l5bEYKolg+DMqVhlOANd8Yh5mkQWaMEvBRE/kAGzIqg
# WhwzN2OsKIVtO8mf5sPWSrvyplSABAYa13rMYnzwfg08nljZHghquCJYCa/xHK9a
# cev9UD7Y+usr15d7mrszzxhF1JOr1Mpup2chNSBlyOObhlSO16rwrffVrg/SzaKf
# SndS5swRhr8bnDqNJY9TNyEYvBYpgF95K7p0g4LguR4A++Z1nFIHWVY5v0fNVZmg
# zxD9uVo/gta3onGOQj3JCxgYx0KrCXu4yc9QiVwTFLWbNdHFSjBCt5/8Q9pLuRhV
# ocdCunhcHudMS1CGQ/Rn0+7P+fzMgWdRKfEOh/hjLrnQ8BdJiYrZNxvIOhM2aa3z
# EDHNwwIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUvlQC
# L79AbHNDzqwJJU6eQ0Qa7uAwHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61PgcptE6
# Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMDAGA1UdHwQp
# MCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYIKwYB
# BQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u
# Y29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3Ru
# Y2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0cDov
# L3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC4k1l3yUwV/ZQH
# CKCneqAs8EGTnwEUJLdDpokN/dMhKjK0rR5qX8nIIHzxpQR3TAw2IRw1Uxsr2Pli
# G3bCFqSdQTUbfaTq6V3vBzEebDru9QFjqlKnxCF2h1jhLNFFplbPJiW+JSnJTh1f
# KEqEdKdxgl9rVTvlxfEJ7exOn25MGbd/wGPwuSmMxRJVO0wnqgS7kmoJjNF9zqee
# hFSDDP8ZVkWg4EZ2tIS0M3uZmByRr+1Lkwjjt8AtW83mVnZTyTsOb+FNfwJY7DS4
# FmWhkRbgcHRetreoTirPOr/ozyDKhT8MTSTf6Lttg6s6T/u08mDWw6HK04ZRDfQ9
# sb77QV8mKgO44WGP31vXnVKoWVJpFBjPvjL8/Zck/5wXX2iqjOaLStFOR/IQki+E
# hn4zlcgVm22ZVCBPF+l8nAwUUShCtKuSU7GmZLKCmmxQMkSiWILTm8EtVD6AxnJh
# oq8EnhjEEyUoflkeRF2WhFiVQOmWTwZRr44IxWGkNJC6tTorW5rl2Zl+2e9JLPYf
# 3pStAPMDoPKIjVXd6NW2+fZrNUBeDo2eOa5Fn7Brs/HLQff5Xgris5MeUbdVgDrF
# 8uxO6cLPvZPo63j62SsNg55pTWk9fUIF9iPoRbb4QurjoY/woI1RAOKtYtTic6aA
# Jq3u83RIPpGXBSJKwx4KJAOZnCDCtTGCBqIwggaeAgEBMGowVjELMAkGA1UEBhMC
# UEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMb
# Q2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBAhBX5gfP8LabvnxCXoItEBa/MA0G
# CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI
# hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC
# ARUwLwYJKoZIhvcNAQkEMSIEIN/gkyNRKjTylyGiNJhGPJS8MxZH0UKd8zwY1Cy0
# tU73MA0GCSqGSIb3DQEBAQUABIIBgEu6Xb1uKSR2BouFQcoh2DdNa7gCRin7yY8i
# +Ok4Q187tcy9uZenDpM55vyioqkGIJajmF7fKS8kUyelKn4vO3lkWSmYPg7BD7LW
# ATWAiZO03AD4IR8wjQ2gcLGTNEZqo5r0JH/mjVXipJ+5fu5LGwNQqgNElx+dYL/m
# LJ50lEtUPb6tm/jPgAiw4B1dAtFvgFF35z+4qldaOl0zX9fQH8zMrGt+ojzYkzKo
# hcVjOjcvcjZXx/Btcquckru5pohuyoxGpoTgBvMBZxBnVfRVb0DbrrUV0w8BJbDV
# wo2wEcFE8G7caPKQJ3/jGKCG6vnMfvS0mQo0gNAJt2GN7toPUMe6EHTw0ZfItfyN
# +ta6Nt+gB5IE3oBY9wz0swdkNxI8iMH5DwGYwxzv7i9lKwQiXH0whUsEjPaxvQQi
# pcAVkIjiw6wCMLPLNSJf24HQyuzZnTiqWTR0WN4GNykmT5rPTkBmvsZzT+9lRsZX
# WaqgtXp7IG/KH/LSZGKqnpl/9WfTfqGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID
# 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0
# ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ
# KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMjcwNTM4MzZaMDcG
# CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY
# 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDDETXmRzJILhuWz4wo8s35CjQ+MrFRfMbUw
# XJuSBwd1d5laeT2BcPNqEhKHLFuVG64wgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ
# MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh
# MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0
# dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI
# hvcNAQEBBQAEggIAkMaGPwF3ljHm6F+l/15vbAx/N/pICFHmksvVm8a9z7+z9RPk
# 93sQrNyxmy3gm2tPlZSfkrKDb3b0A/tssEKq/vU8uEtWB+yDKhHFXoXMMJLXZGlT
# o1Xic4j/SJeqMkx/1jkMSyFTynNCLQ0f+hsGXCF1J1RjVJKpjRLjOG+S1lA2irGK
# 6v3kKheUkoFeXhHd8hSQNay2l2EC6wX1Pss2AGfgOXFj7IyGwWhWMzA5VNlc7zyq
# IWi3oVOTtRTv7pz4EhxszpnTv8i9nAV++EmRExbyBDQP3EQ6xB282e1GCL84yKAb
# wh60Dv2I/CkSuKvWvqCMjvf5zMIx/S6Y7CaxgmIpoKwQ27lQPz2d/KsrAJk7M6gC
# CKucGKs5cFGUpETj+RqVUmFMYx0nmirqNlllw/qqB2YnHjJLOGfKXhwWbQtDeMLK
# xoLYuPUwddeUXs+F/drahqxAt+cpmZSEC9Hj8FiG5m4ZRiLbyCbst65aDcXPKk4V
# /nhKkGMQlYuOBqwRqZoUjPhGZo36qnOp4gEPs0m01NFD+wRqoZMe95I5weZFVp49
# kTsAcu+rhFcSK6BU6gTz7UdpvkUmE467juXGSuBN0fXsDdtO9hM0N/2i+P4d3cJb
# IzBsEX4Sbtej3kKIBsYbUnIR9dPh3gwo7zFoyTbh6jI2LfqNyXh3d5rwQE8=
# SIG # End signature block