public/Get-IvantiWCPolicy.ps1

<#
.SYNOPSIS
    Reads, decodes, and correlates Ivanti Workspace Control policy sets from a Building Block XML file.
.DESCRIPTION
    Reads an Ivanti Workspace Control Building Block XML file, pre-loads all embedded ADMX/ADML
    templates, and processes each policy set found within the file. For each policy set it correlates
    the applied registry settings with their ADMX definitions (via Get-AdmxPolicySetting) and returns
    a structured PowerShell object per policy set.
 
    Supports two export modes via -ExportFor:
    - WEM: returns correlated policy data only (default).
    - AppVentiX: additionally returns an AppVentiXParams property containing pre-structured policy
      data ready to pass directly to New-AppVentiXGroupPolicy -PolicyInputObject.
 
    Policy state (Enabled/Disabled/Unconfigured) is determined in priority order:
    1. POLICY:1/2 indicator from the embedded PolicySettings data.
    2. Policy-level registry value compared against ADMX enabledValue/disabledValue.
    3. Presence of element-level registry values (implies Enabled).
 
    List-type ADMX elements (values stored as numbered entries under a sub-key) are fully supported.
    Boolean elements with explicit trueValue/falseValue nodes are resolved correctly.
.PARAMETER Path
    Path to the Ivanti Workspace Control Building Block XML file.
.PARAMETER IncludeADMFiles
    If specified, includes the ADMX and ADML filenames and their base64-encoded content in the output
    for each policy set. Required when piping output to New-AppVentiXGroupPolicy.
.PARAMETER ExportFor
    Target export format. 'AppVentiX' adds the AppVentiXParams property to each output object.
    Defaults to 'WEM'.
.PARAMETER IncludePolicyDescription
    If specified, includes the ADMX ExplainText (policy description) in the PolicySettings output.
.PARAMETER SaveResourceFiles
    If specified, saves the decoded ADMX/ADML files and the raw PolicySettings/RegistryFile data
    to disk at the path specified by -ExportPath.
.PARAMETER ExportPath
    Directory path where decoded resource files are saved when -SaveResourceFiles is used.
.EXAMPLE
    # Export policy data for use with AppVentiX
    $Policies = Get-IvantiWCPolicy -Path 'C:\temp\LAB-BB.xml' -IncludeADMFiles -ExportFor AppVentiX
    $result = $Policies | ForEach-Object {
        New-AppVentiXGroupPolicy `
            -FriendlyName $_.Name `
            -AdmxContent $_.ADMXContent `
            -AdmxFileName $_.ADMX `
            -AdmlContent $_.ADMLContent `
            -AdmlFileName $_.ADML `
            -PolicyInputObject $_.AppVentiXParams
    }
.EXAMPLE
    # Inspect correlated policy data as JSON
    Get-IvantiWCPolicy -Path 'C:\temp\LAB-BB.xml' | ConvertTo-Json -Depth 5
.EXAMPLE
    # Save decoded resource files for inspection
    Get-IvantiWCPolicy -Path 'C:\temp\LAB-BB.xml' -SaveResourceFiles -ExportPath 'C:\temp\TempPolicy'
.NOTES
    Function : Get-IvantiWCPolicy
    Author : John Billekens
    Copyright : (c) John Billekens Consultancy
    Version : 2026.0308.1500
#>

function Get-IvantiWCPolicy {
    [CmdletBinding(DefaultParameterSetName = 'AppVentiX')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path,

        [switch]$IncludeADMFiles,

        [ValidateSet("AppVentiX", "WEM")]
        [string]$ExportFor = "WEM",

        [switch]$IncludePolicyDescription,

        [Parameter(Mandatory = $true, ParameterSetName = 'Export')]
        [switch]$SaveResourceFiles,

        [Parameter(Mandatory = $true, ParameterSetName = 'Export')]
        [string]$ExportPath
    )

    function Convert-HexToString {
        param([string]$HexString)
        if ([string]::IsNullOrEmpty($HexString)) { return '' }
        $bytes = for ($i = 0; $i -lt $HexString.Length; $i += 2) {
            [System.Convert]::ToByte($HexString.Substring($i, 2), 16)
        }
        return [System.Text.Encoding]::GetEncoding(1252).GetString($bytes)
    }

    function Convert-RegistryHexToValue {
        param(
            [string]$hexString,
            [string]$type
        )

        # Extract hex bytes (remove hex(X): prefix and commas/spaces)
        $hexData = $hexString -replace '^hex\(\w+\):', '' -replace '[,\s]', ''

        if ([string]::IsNullOrEmpty($hexData)) { return $null }

        try {
            $bytes = for ($i = 0; $i -lt $hexData.Length; $i += 2) {
                [System.Convert]::ToByte($hexData.Substring($i, 2), 16)
            }

            switch ($type) {
                'REG_SZ' {
                    # hex(1) - null-terminated Unicode string
                    return [System.Text.Encoding]::Unicode.GetString($bytes).TrimEnd([char]0)
                }
                'REG_EXPAND_SZ' {
                    # hex(2) - expandable string
                    return [System.Text.Encoding]::Unicode.GetString($bytes).TrimEnd([char]0)
                }
                'REG_BINARY' {
                    # hex(3) or hex - binary data
                    return [System.BitConverter]::ToString($bytes)
                }
                'REG_DWORD' {
                    # hex(4) - 32-bit number (little-endian)
                    if ($bytes.Count -eq 4) {
                        return [System.BitConverter]::ToUInt32($bytes, 0)
                    }
                    return $null
                }
                'REG_MULTI_SZ' {
                    # hex(7) - multiple null-terminated strings
                    $fullString = [System.Text.Encoding]::Unicode.GetString($bytes)
                    return ($fullString -split '\0' | Where-Object { $_ })
                }
                'REG_QWORD' {
                    # hex(b) - 64-bit number (little-endian)
                    if ($bytes.Count -eq 8) {
                        return [System.BitConverter]::ToUInt64($bytes, 0)
                    }
                    return $null
                }
                default {
                    return [System.BitConverter]::ToString($bytes)
                }
            }
        } catch {
            Write-Warning "Could not decode hex value for type $type. Error: $_"
            return $hexString
        }
    }

    function Convert-HexToStream {
        param([string]$HexString)
        if ([string]::IsNullOrEmpty($HexString)) { return $null }
        try {
            $bytes = for ($i = 0; $i -lt $HexString.Length; $i += 2) {
                [System.Convert]::ToByte($HexString.Substring($i, 2), 16)
            }
            # A MemoryStream is more efficient for large data than holding a giant string
            $memStream = New-Object System.IO.MemoryStream(, $bytes)
            return $memStream
        } catch {
            Write-Warning "Could not convert hex string to stream. Error: $_"
            return $null
        }
    }

    try {
        if (-not (Test-Path -Path $Path -PathType Leaf)) {
            throw "File not found at path: $Path"
        }

        # Resolve to absolute path for XmlDocument.Load()
        $absolutePath = (Resolve-Path -Path $Path).Path
        Write-Verbose "Loading XML file: $absolutePath"
        $xmlContent = New-Object System.Xml.XmlDocument
        $xmlContent.Load($absolutePath)

        # 1. Pre-load all ADM templates
        Write-Verbose "Pre-loading embedded ADMX/ADML templates..."
        $admTemplateStore = @{}
        $embeddedPolicies = $xmlContent.GetElementsByTagName('embeddedadm')
        $embeddedCount = $embeddedPolicies.Count
        Write-Verbose "Found $embeddedCount embedded template(s)"

        $templateCount = 0
        foreach ($adm in $embeddedPolicies) {
            $fileName = $adm.filename
            if (-not $admTemplateStore.ContainsKey($fileName)) {
                $templateCount++
                Write-Progress -Activity "Loading ADMX templates" -Status "$fileName" -PercentComplete ([int]($templateCount / [Math]::Max($embeddedCount, 1) * 100))
                Write-Verbose "Loading template $templateCount/$embeddedCount`: $fileName"
                $admlFileName = $fileName -replace '.admx$', '.adml'

                $admxStream = Convert-HexToStream -HexString $adm.embeddedbinary
                $admlStream = Convert-HexToStream -HexString $adm.embeddedadml

                if ($null -eq $admxStream) {
                    Write-Warning "Failed to decode ADMX template: $fileName"
                    continue
                }

                try {
                    $admxXml = New-Object System.Xml.XmlDocument
                    $admxXml.Load($admxStream)

                    $admlXml = New-Object System.Xml.XmlDocument
                    if ($null -ne $admlStream) {
                        $admlXml.Load($admlStream)
                    }

                    $admxBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($admxXml.OuterXml))
                    $admlBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($admlXml.OuterXml))

                    # Pre-load all policy settings from this template via Get-AdmxPolicySetting
                    Write-Verbose " Indexing policy settings for: $fileName"
                    $allPolicySettings = Get-AdmxPolicySetting -AdmxContent $admxBase64 -AdmlContent $admlBase64 -AdmxFileName $fileName -AdmlFileName $admlFileName -All

                    # Build lookup: normalized RegistryKey\ValueName -> list of @{Policy; Element} objects
                    # Policy-level valueName maps to Element=$null; element valueName maps to the element object
                    # listLookup: normalized RegistryKey -> list of @{Policy; Element} for 'list' element types
                    $policyLookup = [System.Collections.Hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
                    $listLookup = [System.Collections.Hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
                    foreach ($ps in $allPolicySettings) {
                        # Policy-level (enabledValue/disabledValue controls this valueName)
                        if (-not [string]::IsNullOrEmpty($ps.ValueName)) {
                            $lookupKey = "$($ps.RegistryKey)\$($ps.ValueName)"
                            if (-not $policyLookup.ContainsKey($lookupKey)) {
                                $policyLookup[$lookupKey] = [System.Collections.Generic.List[object]]::new()
                            }
                            $policyLookup[$lookupKey].Add([PSCustomObject]@{ Policy = $ps; Element = $null })
                        }
                        # Element-level entries
                        foreach ($elem in $ps.Elements) {
                            if ($elem.ElementType -eq 'list') {
                                # List elements have no fixed valueName — index by their sub-key
                                $listKey = $elem.RegistryKey
                                if (-not $listLookup.ContainsKey($listKey)) {
                                    $listLookup[$listKey] = [System.Collections.Generic.List[object]]::new()
                                }
                                $listLookup[$listKey].Add([PSCustomObject]@{ Policy = $ps; Element = $elem })
                            } else {
                                $elemLookupKey = "$($elem.RegistryKey)\$($elem.ValueName)"
                                if (-not $policyLookup.ContainsKey($elemLookupKey)) {
                                    $policyLookup[$elemLookupKey] = [System.Collections.Generic.List[object]]::new()
                                }
                                $policyLookup[$elemLookupKey].Add([PSCustomObject]@{ Policy = $ps; Element = $elem })
                            }
                        }
                    }

                    $admTemplateStore[$fileName] = [PSCustomObject]@{
                        ADMXFile     = $fileName
                        ADMLFile     = $admlFileName
                        ADMXContent  = $admxBase64
                        ADMLContent  = $admlBase64
                        PolicyLookup = $policyLookup
                        ListLookup   = $listLookup
                    }

                    if ($SaveResourceFiles.IsPresent -eq $true) {
                        $id = [System.Guid]::NewGuid().ToString()
                        if (-not (Test-Path -Path $ExportPath)) {
                            New-Item -Path $ExportPath -ItemType Directory -Force | Out-Null
                        }
                        $admxExportPath = Join-Path -Path $ExportPath -ChildPath "$($id)_$($fileName)"
                        $admlExportPath = Join-Path -Path $ExportPath -ChildPath "$($id)_$($admlFileName)"
                        $admxXml.Save($admxExportPath)
                        $admlXml.Save($admlExportPath)
                    }
                } finally {
                    # Ensure streams are always closed
                    if ($null -ne $admxStream) { $admxStream.Dispose() }
                    if ($null -ne $admlStream) { $admlStream.Dispose() }
                }
            }
        }

        # 2. Process each policy set
        $registryNodes = @($xmlContent.GetElementsByTagName('registry') | Where-Object { $_.type -eq 'policy' })
        $totalPolicySets = $registryNodes.Count
        Write-Verbose "Processing $totalPolicySets policy set(s)"

        $policySetIndex = 0
        foreach ($registryNode in $registryNodes) {
            $policySetIndex++
            $policySetName = $registryNode.name
            Write-Progress -Activity "Processing policy sets" -Status "$policySetName ($policySetIndex/$totalPolicySets)" -PercentComplete ([int]($policySetIndex / [Math]::Max($totalPolicySets, 1) * 100))
            Write-Verbose "Processing policy set $policySetIndex/$totalPolicySets`: $policySetName"
            $decodedPolicySettingsStr = (Convert-HexToString -HexString $registryNode.policysettings).TrimStart([char]0xFEFF)
            $decodedRegistryFileStr = (Convert-HexToString -HexString $registryNode.registryfile).TrimStart([char]0xFEFF)

            $Assignments = @(ConvertFrom-IvantiAccessControl -AccessControl $registryNode.accesscontrol -IWCComponentName $policySetName -IWCComponent "Policy")

            if ($SaveResourceFiles.IsPresent -eq $true) {
                $id = [System.Guid]::NewGuid().ToString()
                if (-not (Test-Path -Path $ExportPath)) {
                    New-Item -Path $ExportPath -ItemType Directory -Force | Out-Null
                }
                $policySettingsExportPath = Join-Path -Path $ExportPath -ChildPath "$($id)_$($policySetName)_PolicySettings.txt"
                $decodedPolicySettingsStr | Out-File -FilePath $policySettingsExportPath -Encoding UTF8
                $registryFileExportPath = Join-Path -Path $ExportPath -ChildPath "$($id)_$($policySetName)_RegistryFile.txt"
                $decodedRegistryFileStr | Out-File -FilePath $registryFileExportPath -Encoding UTF8
            }

            $admxFileNameMatch = $decodedPolicySettingsStr | Select-String -Pattern "ADMFILENAME:(.*)"
            if (-not $admxFileNameMatch) {
                Write-Warning "Policy set '$policySetName' does not specify an ADMX template file (ADMFILENAME not found)"
                continue
            }
            $admxFileName = $admxFileNameMatch.Matches.Groups[1].Value.Trim()
            Write-Verbose " Using ADMX template: $admxFileName"

            # Parse PolicySettings.txt: build lookup DisplayName -> ConfiguredState
            # Format: POLICY:1:Class||Category|...|DisplayName (1=Enabled, 2=Disabled)
            $policyStateFromSettings = [System.Collections.Hashtable]::new([System.StringComparer]::OrdinalIgnoreCase)
            foreach ($settingsLine in $decodedPolicySettingsStr.Split([string[]]@("`r`n", "`n", "`r"), [StringSplitOptions]::RemoveEmptyEntries)) {
                if ($settingsLine -match '^POLICY:(\d+):[^|]+\|+(.+)$') {
                    $stateCode = $matches[1]
                    $displayPath = $matches[2]
                    $displayName = ($displayPath -split '\|')[-1].Trim()
                    $configuredState = switch ($stateCode) {
                        '1' { 'Enabled' }
                        '2' { 'Disabled' }
                        default { 'Unconfigured' }
                    }
                    if (-not $policyStateFromSettings.ContainsKey($displayName)) {
                        $policyStateFromSettings[$displayName] = $configuredState
                    }
                }
            }
            Write-Verbose " Parsed $($policyStateFromSettings.Count) policy state(s) from PolicySettings"

            if (-not $admTemplateStore.ContainsKey($admxFileName)) {
                Write-Warning "Policy set '$policySetName' references ADMX template '$admxFileName' which was not found in embedded templates"
                continue
            }

            $currentTemplate = $admTemplateStore[$admxFileName]
            $policyLookup = $currentTemplate.PolicyLookup
            $listLookup = $currentTemplate.ListLookup

            $regLines = $decodedRegistryFileStr.Split([string[]]@("`r`n", "`n", "`r"), [StringSplitOptions]::None)
            $currentSectionHive = ''
            $currentSectionKey = ''
            $currentListEntry = $null   # non-null when we're inside a list element sub-key section
            $currentListValues = $null   # accumulates string values for the current list section

            # Collect all matched registry entries grouped by PolicyName
            # Key: PolicyName, Value: @{ Policy; PolicyLevelEntry; ElementEntries: list }
            $policyGroups = [System.Collections.Generic.Dictionary[string, hashtable]]::new()

            for ($i = 0; $i -lt $regLines.Length; $i++) {
                $line = $regLines[$i]

                # Track the current section header (optional leading ! = delete key marker)
                if ($line -match '^!?\[((HKEY_[^\\]+)\\(.+?))\]') {
                    # If we just finished a list section, commit each item as a separate element entry
                    if ($null -ne $currentListEntry -and $null -ne $currentListValues -and $currentListValues.Count -gt 0) {
                        $listMatchedPolicy = $currentListEntry.Policy
                        $listMatchedElement = $currentListEntry.Element

                        if (-not $policyGroups.ContainsKey($listMatchedPolicy.PolicyName)) {
                            $policyGroups[$listMatchedPolicy.PolicyName] = @{
                                Policy           = $listMatchedPolicy
                                PolicyLevelEntry = $null
                                ElementEntries   = [System.Collections.Generic.List[PSCustomObject]]::new()
                                PFCategory       = ''
                                PFDisplayText    = ''
                            }
                        }
                        $policyGroups[$listMatchedPolicy.PolicyName].ElementEntries.Add([PSCustomObject]@{
                                ElementId          = $listMatchedElement.ElementId
                                ElementType        = 'list'
                                ConfiguredState    = 'Enabled'
                                Delete             = $false
                                RegistryHive       = $currentSectionHive
                                RegistryKey        = $listMatchedElement.RegistryKey
                                RegistryKeyPath    = "$currentSectionHive\$($listMatchedElement.RegistryKey)"
                                RegistryValueName  = ''
                                RegistryValue      = @($currentListValues | ForEach-Object { $_.Value })
                                RegistryValueNames = @($currentListValues | ForEach-Object { $_.ValueName })
                                RegistryValueType  = 'REG_SZ'
                                RegistryType       = $listMatchedElement.RegistryType
                                Constraints        = $listMatchedElement.Constraints
                                EnumItems          = $null
                                TrueValue          = $null
                                FalseValue         = $null
                            })
                        Write-Verbose " Committed list element '$($listMatchedElement.ElementId)' with $($currentListValues.Count) item(s) for policy '$($listMatchedPolicy.PolicyName)'"
                    }

                    $currentSectionHive = $matches[2]
                    $currentSectionKey = $matches[3]

                    # Check if this new section is a list element sub-key
                    $normalizedSectionKey = $currentSectionKey -replace '^(HKEY_LOCAL_MACHINE|HKLM|HKEY_CURRENT_USER|HKCU)\\', ''
                    $listCandidates = $listLookup[$normalizedSectionKey]
                    if ($listCandidates -and $listCandidates.Count -gt 0) {
                        $currentListEntry = $listCandidates[0]   # take first match; list sub-keys are unique
                        $currentListValues = [System.Collections.Generic.List[object]]::new()
                        Write-Verbose " Entering list element section: '$normalizedSectionKey' -> '$($currentListEntry.Policy.PolicyName)' element '$($currentListEntry.Element.ElementId)'"
                    } else {
                        $currentListEntry = $null
                        $currentListValues = $null
                    }
                }

                # Handle deletion markers
                $isDeletion = $false
                if ($line.TrimStart().StartsWith('!"')) {
                    $isDeletion = $true
                    $line = $line.TrimStart().Substring(1)
                    Write-Verbose " Found deletion marker at line $($i + 1)"
                }

                if ($line -match '^"([^"]+)"=(.+)') {
                    $policyNameFromReg = $matches[1]
                    $rawValue = $matches[2].Trim()

                    # If we're inside a list element section, collect each item as {ValueName; Value}
                    if ($null -ne $currentListEntry -and $policyNameFromReg -match '^\d+$') {
                        $itemValue = if ($rawValue.StartsWith('"') -and $rawValue.EndsWith('"')) {
                            $rawValue.Substring(1, $rawValue.Length - 2)
                        } else { $rawValue }
                        $currentListValues.Add([PSCustomObject]@{ ValueName = $policyNameFromReg; Value = $itemValue })
                        continue
                    }

                    # Look for PF comment on the next line
                    $pfComment = ''
                    $pfCategory = ''
                    $pfDisplayText = ''
                    if (($i + 1) -lt $regLines.Length -and $regLines[$i + 1].TrimStart().StartsWith(';<PF>')) {
                        if ($regLines[$i + 1] -match ';<PF>(.*)</PF>') {
                            $pfComment = $matches[1]
                            if ($pfComment -match '^(HKEY_[^\\]+)\\([^\\]+)\\(.+)$') {
                                $pfCategory = $matches[2]
                                $pfDisplayText = $matches[3]
                            }
                        }
                    }

                    $registryValue = $null
                    $registryValueType = 'Unknown'

                    if ($rawValue.StartsWith('dword:') -or $rawValue.StartsWith('qword:')) {
                        $registryValueType = if ($rawValue.StartsWith('dword:')) { 'REG_DWORD' } else { 'REG_QWORD' }
                        try {
                            $registryValue = [uint64]("0x" + $rawValue.Split(':')[1])
                        } catch {
                            Write-Warning "Could not parse numeric value for '$policyNameFromReg': $rawValue. Error: $_"
                            $registryValue = $rawValue
                            $registryValueType = 'Unknown'
                        }
                    } elseif ($rawValue.StartsWith('"') -and $rawValue.EndsWith('"')) {
                        $registryValueType = 'REG_SZ'
                        $registryValue = $rawValue.Substring(1, $rawValue.Length - 2)
                    } elseif ($rawValue.StartsWith('hex')) {
                        if ($rawValue.StartsWith('hex(1):')) {
                            $registryValueType = 'REG_SZ'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_SZ'
                        } elseif ($rawValue.StartsWith('hex(2):')) {
                            $registryValueType = 'REG_EXPAND_SZ'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_EXPAND_SZ'
                        } elseif ($rawValue.StartsWith('hex(3):') -or $rawValue.StartsWith('hex:')) {
                            $registryValueType = 'REG_BINARY'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_BINARY'
                        } elseif ($rawValue.StartsWith('hex(4):')) {
                            $registryValueType = 'REG_DWORD'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_DWORD'
                        } elseif ($rawValue.StartsWith('hex(7):')) {
                            $registryValueType = 'REG_MULTI_SZ'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_MULTI_SZ'
                        } elseif ($rawValue.StartsWith('hex(b):')) {
                            $registryValueType = 'REG_QWORD'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_QWORD'
                        } else {
                            $registryValueType = 'REG_BINARY'
                            $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_BINARY'
                        }
                    } else {
                        $registryValueType = 'REG_SZ'
                        $registryValue = $rawValue
                    }

                    # Lookup policy via normalized key\valueName
                    $normalizedKey = $currentSectionKey -replace '^(HKEY_LOCAL_MACHINE|HKLM|HKEY_CURRENT_USER|HKCU)\\', ''
                    $lookupKey = "$normalizedKey\$policyNameFromReg"
                    $candidatePolicies = $policyLookup[$lookupKey]

                    $matchedEntry = $null
                    if ($candidatePolicies -and $candidatePolicies.Count -gt 1) {
                        $policyNames = ($candidatePolicies | ForEach-Object { $_.Policy.PolicyName }) -join ', '

                        if ($pfComment) {
                            foreach ($candidate in $candidatePolicies) {
                                if ($pfComment -match [regex]::Escape($candidate.Policy.RegistryKey)) {
                                    $matchedEntry = $candidate
                                    Write-Verbose " Selected '$($candidate.Policy.PolicyName)' from multiple matches ($policyNames) based on registry key path match"
                                    break
                                }
                            }
                        }

                        if (-not $matchedEntry) {
                            $nonRecommended = $candidatePolicies | Where-Object { $_.Policy.PolicyName -notmatch '_recommended$' } | Select-Object -First 1
                            if ($nonRecommended) {
                                $matchedEntry = $nonRecommended
                                Write-Verbose " Selected '$($matchedEntry.Policy.PolicyName)' from multiple matches ($policyNames) - preferred non-recommended policy"
                            }
                        }

                        if (-not $matchedEntry) {
                            $matchedEntry = $candidatePolicies[0]
                            Write-Warning " Multiple policies found for '$lookupKey': $policyNames. Selected '$($matchedEntry.Policy.PolicyName)' (first match)"
                        }
                    } else {
                        $matchedEntry = $candidatePolicies | Select-Object -First 1
                    }

                    if ($matchedEntry) {
                        $matchedPolicy = $matchedEntry.Policy
                        $matchedElement = $matchedEntry.Element  # $null = policy-level match

                        Write-Verbose " Matched policy: $($matchedPolicy.PolicyName) ($(if ($null -ne $matchedElement) { $matchedElement.ElementType } else { 'policy-level' }))"

                        # Ensure group entry exists for this policy
                        if (-not $policyGroups.ContainsKey($matchedPolicy.PolicyName)) {
                            $policyGroups[$matchedPolicy.PolicyName] = @{
                                Policy           = $matchedPolicy
                                PolicyLevelEntry = $null   # filled when policy-level valueName is matched
                                ElementEntries   = [System.Collections.Generic.List[PSCustomObject]]::new()
                                PFCategory       = $pfCategory
                                PFDisplayText    = $pfDisplayText
                            }
                        }

                        if ($null -eq $matchedElement) {
                            # Policy-level match (NoAutoUpdate, etc.) — determines ConfiguredState of the policy
                            $configuredState = 'Unconfigured'
                            if ($null -ne $matchedPolicy.EnabledValue -and "$registryValue" -eq "$($matchedPolicy.EnabledValue)") {
                                $configuredState = 'Enabled'
                            } elseif ($null -ne $matchedPolicy.DisabledValue -and "$registryValue" -eq "$($matchedPolicy.DisabledValue)") {
                                $configuredState = 'Disabled'
                            } elseif ($registryValueType -in @('REG_DWORD', 'REG_QWORD')) {
                                $configuredState = if ($registryValue -ne 0) { 'Enabled' } else { 'Disabled' }
                            } else {
                                $configuredState = 'Enabled'
                            }

                            $policyGroups[$matchedPolicy.PolicyName].PolicyLevelEntry = [PSCustomObject]@{
                                ConfiguredState   = $configuredState
                                Delete            = $isDeletion
                                RegistryHive      = $currentSectionHive
                                RegistryKey       = $matchedPolicy.RegistryKey
                                RegistryKeyPath   = "$currentSectionHive\$($matchedPolicy.RegistryKey)"
                                RegistryValueName = $policyNameFromReg
                                RegistryValue     = $registryValue
                                RegistryValueType = $registryValueType
                            }
                        } else {
                            # Element-level match
                            $elemType = $matchedElement.ElementType

                            # Determine ConfiguredState for boolean elements with explicit trueValue/falseValue
                            $configuredState = if ($elemType -eq 'boolean' -and $null -ne $matchedElement.TrueValue -and $null -ne $matchedElement.FalseValue) {
                                if ("$registryValue" -eq "$($matchedElement.TrueValue)") { 'Enabled' }
                                elseif ("$registryValue" -eq "$($matchedElement.FalseValue)") { 'Disabled' }
                                else { 'Enabled' }  # presence implies enabled
                            } else {
                                'Enabled'  # presence of any element value means policy is enabled
                            }

                            $elemEntry = [PSCustomObject]@{
                                ElementId         = $matchedElement.ElementId
                                ElementType       = $elemType
                                ConfiguredState   = $configuredState
                                Delete            = $isDeletion
                                RegistryHive      = $currentSectionHive
                                RegistryKey       = $matchedElement.RegistryKey
                                RegistryKeyPath   = "$currentSectionHive\$($matchedElement.RegistryKey)"
                                RegistryValueName = $policyNameFromReg
                                RegistryValue     = $registryValue
                                RegistryValueType = $registryValueType
                                RegistryType      = $matchedElement.RegistryType
                                Constraints       = $matchedElement.Constraints
                                EnumItems         = $matchedElement.EnumItems
                                TrueValue         = $matchedElement.TrueValue
                                FalseValue        = $matchedElement.FalseValue
                            }
                            $policyGroups[$matchedPolicy.PolicyName].ElementEntries.Add($elemEntry)
                        }
                    } else {
                        Write-Warning " Registry value '$policyNameFromReg' does not match any policy in ADMX template '$admxFileName'"
                    }
                }
            }

            # Commit any pending list section after the last line
            if ($null -ne $currentListEntry -and $null -ne $currentListValues -and $currentListValues.Count -gt 0) {
                $listMatchedPolicy = $currentListEntry.Policy
                $listMatchedElement = $currentListEntry.Element

                if (-not $policyGroups.ContainsKey($listMatchedPolicy.PolicyName)) {
                    $policyGroups[$listMatchedPolicy.PolicyName] = @{
                        Policy           = $listMatchedPolicy
                        PolicyLevelEntry = $null
                        ElementEntries   = [System.Collections.Generic.List[PSCustomObject]]::new()
                        PFCategory       = ''
                        PFDisplayText    = ''
                    }
                }
                $policyGroups[$listMatchedPolicy.PolicyName].ElementEntries.Add([PSCustomObject]@{
                        ElementId          = $listMatchedElement.ElementId
                        ElementType        = 'list'
                        ConfiguredState    = 'Enabled'
                        Delete             = $false
                        RegistryHive       = $currentSectionHive
                        RegistryKey        = $listMatchedElement.RegistryKey
                        RegistryKeyPath    = "$currentSectionHive\$($listMatchedElement.RegistryKey)"
                        RegistryValueName  = ''
                        RegistryValue      = @($currentListValues | ForEach-Object { $_.Value })
                        RegistryValueNames = @($currentListValues | ForEach-Object { $_.ValueName })
                        RegistryValueType  = 'REG_SZ'
                        RegistryType       = $listMatchedElement.RegistryType
                        Constraints        = $listMatchedElement.Constraints
                        EnumItems          = $null
                        TrueValue          = $null
                        FalseValue         = $null
                    })
                Write-Verbose " Committed list element '$($listMatchedElement.ElementId)' with $($currentListValues.Count) item(s) for policy '$($listMatchedPolicy.PolicyName)'"
            }

            # Build correlated settings: 1 object per policy
            $correlatedSettings = foreach ($group in $policyGroups.Values) {
                $admxPolicy = $group.Policy
                $policyEntry = $group.PolicyLevelEntry
                $elementEntries = $group.ElementEntries

                # Determine overall policy ConfiguredState
                # Priority: 1) PolicySettings.txt POLICY:1/2 indicator, 2) policy-level registry entry, 3) element presence
                $overallState = if ($policyStateFromSettings.ContainsKey($admxPolicy.DisplayName)) {
                    $policyStateFromSettings[$admxPolicy.DisplayName]
                } elseif ($null -ne $policyEntry) {
                    $policyEntry.ConfiguredState
                } elseif ($elementEntries.Count -gt 0) {
                    'Enabled'
                } else {
                    'Unconfigured'
                }

                [PSCustomObject]([ordered]@{
                        PolicyName        = $admxPolicy.PolicyName
                        DisplayName       = $admxPolicy.DisplayName
                        ConfiguredState   = $overallState
                        Class             = $admxPolicy.Class
                        Category          = $admxPolicy.Category
                        SupportedOn       = $admxPolicy.SupportedOn
                        Description       = if ($IncludePolicyDescription) { $admxPolicy.ExplainText } else { $null }
                        EnabledValue      = $admxPolicy.EnabledValue
                        DisabledValue     = $admxPolicy.DisabledValue
                        RegistryHive      = if ($null -ne $policyEntry) { $policyEntry.RegistryHive } else { $null }
                        RegistryKey       = $admxPolicy.RegistryKey
                        RegistryKeyPath   = if ($null -ne $policyEntry) { $policyEntry.RegistryKeyPath } else { $null }
                        RegistryValueName = if ($null -ne $policyEntry) { $policyEntry.RegistryValueName } else { $null }
                        RegistryValue     = if ($null -ne $policyEntry) { $policyEntry.RegistryValue } else { $null }
                        RegistryValueType = if ($null -ne $policyEntry) { $policyEntry.RegistryValueType } else { $null }
                        Delete            = if ($null -ne $policyEntry) { $policyEntry.Delete } else { $false }
                        Elements          = if ($elementEntries.Count -gt 0) { $elementEntries.ToArray() } else { @() }
                        PFCategory        = $group.PFCategory
                        PFDisplayText     = $group.PFDisplayText
                    })
            }

            # Extract policy set metadata
            $runOnce = ($registryNode.runonce -eq 'yes')
            $setEnabled = ($registryNode.enabled -eq 'yes')
            $guid = $registryNode.guid

            $policySetOutput = [ordered]@{
                Name           = $registryNode.name
                Description    = $registryNode.description
                RunOnce        = $runOnce
                Enabled        = $setEnabled
                GUID           = $guid
                PolicySettings = $correlatedSettings
            }

            if ($ExportFor -eq 'AppVentiX') {
                $policySetOutput.AppVentiXAssignments = $Assignments
                $policySetOutput.AppVentiXParams = @()
                foreach ($policySetting in $correlatedSettings) {
                    # Element-level registry entries
                    $AppVentiXPolicyElements = @()
                    $AppVentiXPolicyRegistryKeys = @()
                    if ((-not [string]::IsNullOrEmpty($policySetting.RegistryHive)) -and (-not [string]::IsNullOrEmpty($policySetting.RegistryKey)) -and (-not [string]::IsNullOrEmpty($policySetting.RegistryValueName)) ) {
                        $AppVentiXPolicyRegistryKeys += [PSCustomObject]@{
                                RootKey   = $policySetting.RegistryHive
                                KeyPath   = $policySetting.RegistryKey
                                ValueName = $policySetting.RegistryValueName
                                ValueData = $policySetting.RegistryValue
                                ValueType = $policySetting.RegistryValueType
                                Action    = "Set"
                                DeleteKey = $policySetting.Delete
                            }
                    }

                    foreach ($element in $policySetting.Elements) {
                        if ($element.ElementType -eq 'list') {
                            $AppVentiXPolicyElements += [PSCustomObject]@{
                                    ElementId         = $element.ElementId
                                    RegistryKey       = $element.RegistryKey
                                    RegistryValueName = ''
                                    Value             = $element.RegistryValue -join [System.Environment]::NewLine
                                    ValueType         = $element.RegistryValueType
                                }
                            if (-not [string]::IsNullOrEmpty($element.RegistryHive) -and -not [string]::IsNullOrEmpty($element.RegistryKey)) {
                                for ($idx = 0; $idx -lt $element.RegistryValue.Count; $idx++) {
                                    $AppVentiXPolicyRegistryKeys += [PSCustomObject]@{
                                            RootKey   = $element.RegistryHive
                                            KeyPath   = $element.RegistryKey
                                            ValueName = $element.RegistryValueNames[$idx]
                                            ValueData = $element.RegistryValue[$idx]
                                            ValueType = $element.RegistryType
                                            Action    = "Set"
                                            DeleteKey = $element.Delete
                                        }
                                }
                            }
                        } else {
                            $ElementValue = $null
                            $ElementValueType = $element.RegistryValueType
                            if ($null -ne $element.EnumItems -and $element.EnumItems.Count -gt 0) {
                                try {
                                    #$ElementValue = ($element.EnumItems | Where-Object { $_.RegistryValue -eq $element.RegistryValue }).DisplayName
                                    $ElementValue = $element.RegistryValue
                                    $ElementValueType = 'REG_SZ'
                                } catch { $null }
                            } elseif ($element.RegistryValueType -in @('REG_DWORD', 'REG_QWORD')) {
                                $ElementValue = $element.RegistryValue -ne 0
                            } elseif ($element.RegistryValueType -eq 'REG_SZ') {
                                $ElementValue = $element.RegistryValue
                            } else {
                                Write-Warning "Policy '$($registryNode.name)' Element '$($element.ElementId)' has unsupported RegistryValueType '$($element.RegistryValueType)' for AppVentiX export. Contact AppventiX Support for assistance."
                            }
                            $AppVentiXPolicyElements += [PSCustomObject]@{
                                    ElementId         = $element.ElementId
                                    RegistryKey       = $element.RegistryKey
                                    RegistryValueName = $element.RegistryValueName
                                    Value             = $ElementValue
                                    ValueType         = $ElementValueType
                                }
                            if ((-not [string]::IsNullOrEmpty($element.RegistryHive)) -and (-not [string]::IsNullOrEmpty($element.RegistryKey)) -and (-not [string]::IsNullOrEmpty($element.RegistryValueName))) {
                                $AppVentiXPolicyRegistryKeys += [PSCustomObject]@{
                                        RootKey   = $element.RegistryHive
                                        KeyPath   = $element.RegistryKey
                                        ValueName = $element.RegistryValueName
                                        ValueData = $element.RegistryValue
                                        ValueType = $element.RegistryType
                                        Action    = "Set"
                                        DeleteKey = $element.Delete
                                    }
                            }
                        }
                    }
                    # Policy-level registry entry
                    if ($null -ne $policySetting.PolicyName) {
                        $policySetOutput.AppVentiXParams += [PSCustomObject]@{
                            PolicyName        = $policySetting.PolicyName
                            PolicyDisplayName = $policySetting.DisplayName
                            PolicyKey         = $policySetting.RegistryKey
                            PolicyValueName   = $policySetting.RegistryValueName
                            Enabled           = $policySetting.ConfiguredState -eq 'Enabled'
                            Elements          = @($AppVentiXPolicyElements)
                            RegistryEntries   = @($AppVentiXPolicyRegistryKeys)
                        }
                    }
                }
            }
            if ($ExportFor -eq 'WEM') {
                Write-Warning "WEM export format is not implemented yet!"
            }

            if ($IncludeADMFiles) {
                $policySetOutput.ADMX = $currentTemplate.ADMXFile
                $policySetOutput.ADML = $currentTemplate.ADMLFile
                $policySetOutput.ADMXContent = $currentTemplate.ADMXContent
                $policySetOutput.ADMLContent = $currentTemplate.ADMLContent
            }

            Write-Verbose "Completed processing policy set: $policySetName"
            Write-Output ([PSCustomObject]$policySetOutput)
        }

        Write-Progress -Activity "Processing policy sets" -Completed
        Write-Verbose "Successfully processed $totalPolicySets policy set(s)"

    } catch {
        $resolvedPath = Resolve-Path -Path $Path -ErrorAction SilentlyContinue
        $filePath = if ($resolvedPath) { $resolvedPath.Path } else { $Path }

        $errorDetails = @(
            "Error processing Ivanti policy file",
            "File: $filePath",
            "Error: $($_.Exception.Message)",
            "Line: $($_.InvocationInfo.ScriptLineNumber)",
            "Position: $($_.InvocationInfo.PositionMessage)"
        )
        Write-Error ($errorDetails -join "`n")
        throw
    }
}

# SIG # Begin signature block
# MII6wAYJKoZIhvcNAQcCoII6sTCCOq0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBDlcQarG9UobQ1
# Fw+LGianBCOvhwmNJ6NZfHeI1A2E0qCCIuQwggXMMIIDtKADAgECAhBUmNLR1FsZ
# 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
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggcGMIIE7qADAgECAhMzAAfM8rDv
# Jyd7QZv4AAAAB8zyMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDIwHhcNMjYwNDAyMjE0MTIwWhcNMjYwNDA1
# MjE0MTIwWjCBgzELMAkGA1UEBhMCTkwxFjAUBgNVBAgTDU5vb3JkLUJyYWJhbnQx
# EjAQBgNVBAcTCVNjaGlqbmRlbDEjMCEGA1UEChMaSm9obiBCaWxsZWtlbnMgQ29u
# c3VsdGFuY3kxIzAhBgNVBAMTGkpvaG4gQmlsbGVrZW5zIENvbnN1bHRhbmN5MIIB
# ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAql2zC5qD+WbLZzawVOEM6hmw
# E525pZhueIRBk85Br6ZG8FoUZl7WzaPQV6JlW6kukWABJbvNbIKzQnLhu+SmCtBF
# 5OA+rjaAREtoajEbtuL+N7rmNVrPTe7POYz2lgLn3VL/0l+xTMI402gfo/Vvha2H
# ItsKRA93A7FHmWxnnhyA7p2ucpLfJNdNoCK1XsB22sNdhTqV8/TWRTuGzYP+FvTp
# jhdcq/xyy16jKdwIgNOXCCAU9U4EO1TLR1WTTmxQNOWy5dNdP48HYLiACx/d9Gf1
# EVytoAgxmsNon12TmLU/FW1KUddP91DcbCmvEg0Zmab/UBPg0bRRufv8oGeJqnb9
# bcCUxCH99cPAvIe0MLg4qCtRM80fA36yz4/dfIC7xowKEUxhR7/kWUfg+3mKA2nJ
# F6qSv3+llYtt7O6GRPWD40Rk9LGbSQD3+uCuAIBUNqh4Sokq1q+j+8jjU4yBNFBc
# tL1HsI6KXgTLWBO81v+mX+Fs62Euo8AfRByW7HhRAgMBAAGjggIZMIICFTAMBgNV
# HRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA8BgNVHSUENTAzBgorBgEEAYI3YQEA
# BggrBgEFBQcDAwYbKwYBBAGCN2G789NTgYr4ukmC0/31KIOytcN0MB0GA1UdDgQW
# BBQKkf4WVoYJs/qIk34nGHoMkkbjSzAfBgNVHSMEGDAWgBRln1HOhWh/L4pFiKrd
# pzG7Hg0AXjBnBgNVHR8EYDBeMFygWqBYhlZodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBF
# T0MlMjBDQSUyMDAyLmNybDCBpQYIKwYBBQUHAQEEgZgwgZUwZAYIKwYBBQUHMAKG
# WGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0
# JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0ElMjAwMi5jcnQwLQYIKwYB
# BQUHMAGGIWh0dHA6Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDBmBgNVHSAE
# XzBdMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wCAYGZ4EMAQQB
# MA0GCSqGSIb3DQEBDAUAA4ICAQC74nf6TvL5WCfrJh27QL0oRpanrcasjQ1NTjWc
# SfyFdOv0IxPLaTcDfmin4wwOwgESPhnSPNWDwFf7tatAOG6pTL4MG2Op+izJPbGa
# ap0Hht3E0qTTsgaoxC2CEgnfeInM2SCKIbf7ymTd76TfqQSDUQdz25Z343yKOPLT
# 5WI/krGe4zCQh+ObMqhTfRTi31YQa+k9fRCALCGcVuu6nrfEC36y5zh8OmxbmawY
# L/CgWg2V5OYx5EA3WXgFnJbgrt3YpL6rxNKcFTvqmqEkc5X1g6CucGXLkAH1x2CC
# HoPQ/D5212vdFoPXLhHjASEbIi5VyzA77rFJF1ahcWO+pHI5YyQ4pvVrPCQ2im1D
# vsihP12hTrlHxdSQnB57pBvKHPRyZdKIxl0eurYdsNIqdspSi0VuWbLB+xUwJhZf
# cSmFBg9uIGVrQrt9MHAKzmcSAJQJn70BmaMmOgR5JbYsDOU4TIRg0p7h5XKWTvso
# 1wGR/Ondg7CdFb68kfjNhUAZtCUNd4HdxcgVqkeaLV6teIpNChYu2EUuzOAG7P1V
# hdz3fhn6WoKU6Yaj6mkIPOcBBJzM3ecoriYrrg2GQePxgWJltU6+zwA2PRrqzUzJ
# Brkln75enYK+/KjaCrNE98LiX2/aHHkFDCxd2fwl47iSkdSR57klynfkL+28Lf2e
# WhjDFzCCBwYwggTuoAMCAQICEzMAB8zysO8nJ3tBm/gAAAAHzPIwDQYJKoZIhvcN
# AQEMBQAwWjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjErMCkGA1UEAxMiTWljcm9zb2Z0IElEIFZlcmlmaWVkIENTIEVPQyBDQSAw
# MjAeFw0yNjA0MDIyMTQxMjBaFw0yNjA0MDUyMTQxMjBaMIGDMQswCQYDVQQGEwJO
# TDEWMBQGA1UECBMNTm9vcmQtQnJhYmFudDESMBAGA1UEBxMJU2NoaWpuZGVsMSMw
# IQYDVQQKExpKb2huIEJpbGxla2VucyBDb25zdWx0YW5jeTEjMCEGA1UEAxMaSm9o
# biBCaWxsZWtlbnMgQ29uc3VsdGFuY3kwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQCqXbMLmoP5ZstnNrBU4QzqGbATnbmlmG54hEGTzkGvpkbwWhRmXtbN
# o9BXomVbqS6RYAElu81sgrNCcuG75KYK0EXk4D6uNoBES2hqMRu24v43uuY1Ws9N
# 7s85jPaWAufdUv/SX7FMwjjTaB+j9W+FrYci2wpED3cDsUeZbGeeHIDuna5ykt8k
# 102gIrVewHbaw12FOpXz9NZFO4bNg/4W9OmOF1yr/HLLXqMp3AiA05cIIBT1TgQ7
# VMtHVZNObFA05bLl010/jwdguIALH930Z/URXK2gCDGaw2ifXZOYtT8VbUpR10/3
# UNxsKa8SDRmZpv9QE+DRtFG5+/ygZ4mqdv1twJTEIf31w8C8h7QwuDioK1EzzR8D
# frLPj918gLvGjAoRTGFHv+RZR+D7eYoDackXqpK/f6WVi23s7oZE9YPjRGT0sZtJ
# APf64K4AgFQ2qHhKiSrWr6P7yONTjIE0UFy0vUewjopeBMtYE7zW/6Zf4WzrYS6j
# wB9EHJbseFECAwEAAaOCAhkwggIVMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD
# AgeAMDwGA1UdJQQ1MDMGCisGAQQBgjdhAQAGCCsGAQUFBwMDBhsrBgEEAYI3Ybvz
# 01OBivi6SYLT/fUog7K1w3QwHQYDVR0OBBYEFAqR/hZWhgmz+oiTficYegySRuNL
# MB8GA1UdIwQYMBaAFGWfUc6FaH8vikWIqt2nMbseDQBeMGcGA1UdHwRgMF4wXKBa
# oFiGVmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29m
# dCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEVPQyUyMENBJTIwMDIuY3JsMIGlBggr
# BgEFBQcBAQSBmDCBlTBkBggrBgEFBQcwAoZYaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ1Ml
# MjBFT0MlMjBDQSUyMDAyLmNydDAtBggrBgEFBQcwAYYhaHR0cDovL29uZW9jc3Au
# bWljcm9zb2Z0LmNvbS9vY3NwMGYGA1UdIARfMF0wUQYMKwYBBAGCN0yDfQEBMEEw
# PwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9j
# cy9SZXBvc2l0b3J5Lmh0bTAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBALvi
# d/pO8vlYJ+smHbtAvShGlqetxqyNDU1ONZxJ/IV06/QjE8tpNwN+aKfjDA7CARI+
# GdI81YPAV/u1q0A4bqlMvgwbY6n6LMk9sZpqnQeG3cTSpNOyBqjELYISCd94iczZ
# IIoht/vKZN3vpN+pBINRB3PblnfjfIo48tPlYj+SsZ7jMJCH45syqFN9FOLfVhBr
# 6T19EIAsIZxW67qet8QLfrLnOHw6bFuZrBgv8KBaDZXk5jHkQDdZeAWcluCu3dik
# vqvE0pwVO+qaoSRzlfWDoK5wZcuQAfXHYIIeg9D8PnbXa90Wg9cuEeMBIRsiLlXL
# MDvusUkXVqFxY76kcjljJDim9Ws8JDaKbUO+yKE/XaFOuUfF1JCcHnukG8oc9HJl
# 0ojGXR66th2w0ip2ylKLRW5ZssH7FTAmFl9xKYUGD24gZWtCu30wcArOZxIAlAmf
# vQGZoyY6BHkltiwM5ThMhGDSnuHlcpZO+yjXAZH86d2DsJ0VvryR+M2FQBm0JQ13
# gd3FyBWqR5otXq14ik0KFi7YRS7M4Abs/VWF3Pd+GfpagpTphqPqaQg85wEEnMzd
# 5yiuJiuuDYZB4/GBYmW1Tr7PADY9GurNTMkGuSWfvl6dgr78qNoKs0T3wuJfb9oc
# eQUMLF3Z/CXjuJKR1JHnuSXKd+Qv7bwt/Z5aGMMXMIIHWjCCBUKgAwIBAgITMwAA
# AAX7elwyE2HfXQAAAAAABTANBgkqhkiG9w0BAQwFADBjMQswCQYDVQQGEwJVUzEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTQwMgYDVQQDEytNaWNyb3Nv
# ZnQgSUQgVmVyaWZpZWQgQ29kZSBTaWduaW5nIFBDQSAyMDIxMB4XDTIxMDQxMzE3
# MzE1M1oXDTI2MDQxMzE3MzE1M1owWjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjErMCkGA1UEAxMiTWljcm9zb2Z0IElEIFZlcmlm
# aWVkIENTIEVPQyBDQSAwMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# ANIamXw/MpCkwOgg21r7IZgaQ6AX+y79d9kVbkqlcEDt2gCy5LkZ50yKEZjBqNHy
# DEK7cEelBwdOsXajpAH6gensbm10x7uOPnbh1AnROJ4IxCwUMpbjLkjpx2hHb3iG
# 7d9ToTBF/+J0JKYePgIdHkeB9rE0a1Spiq5TqEJYYmu90INnR6IrXF9bk9b1typ6
# bfTLe+h+2R2yzbKTGS5FgWmyjJegXWlmV2KTxnUvqmPNIA2jQrxatHddFDLu8R3r
# CKl6Uw090NwHjsUspUk3P++D96IPY9zrnphBWrhcy+ebdTL6WnRIUJg/LhTMRb/4
# mTo8L3HWNc4Wp1Sy8qZc9d/zc0qFN0I4T3agBoUSMIf2A86N4wnVSYFkEa1vNz96
# lhRZMTYjBNojuCLmII+0cLHhsL+yPXMTsmh4oSjHU/TXqEo6Ujc1ZyihnGDLTnTL
# ShaqFewoptpvOBpiGGUwaK0fz6PYsJWrE2ChaRmanVismJyTthVfKDJlpCCtYmoC
# y4BLemWkdK5P9Y1GLLMk4kalq/OELN1y/1eSlARLVBEH0P+EbZyNPbhRVCBiWVfZ
# g/8TlJLFqHRqwkEnpuh8tutcTCQB6k6jLgyoMIvMZP5n5a+8oVzy+90jlC5rtQKr
# uCOxqXukMDVtk7UAcTN77Uk5nT1uqyuctIBA+dUY7MitAgMBAAGjggIOMIICCjAO
# BgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGWfUc6F
# aH8vikWIqt2nMbseDQBeMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwEgYDVR0TAQH/BAgwBgEB
# /wIBADAfBgNVHSMEGDAWgBTZQSmwDw9jbO9p1/XNKZ6kSGow5jBwBgNVHR8EaTBn
# MGWgY6Bhhl9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNy
# b3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAy
# MDIxLmNybDCBrgYIKwYBBQUHAQEEgaEwgZ4wbQYIKwYBBQUHMAKGYWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwSUQlMjBW
# ZXJpZmllZCUyMENvZGUlMjBTaWduaW5nJTIwUENBJTIwMjAyMS5jcnQwLQYIKwYB
# BQUHMAGGIWh0dHA6Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG
# 9w0BAQwFAAOCAgEARUlg1HdPF+IOR871X6wdUolfKZOlzx0/Yz5Apr1P7rwc5mUQ
# I2JNoCmEd6V6ErUSWif5YYwuQ6v9kez9WEWRwg2MitM70kTHROmELYBo5rHYcw9J
# wkuoVVXIH5jULHDydVh8W7xuREIGENGRA9RTdDDj0M1ntc2x+VWtHUsbZ6HQVUY5
# 8g2BJu7DRHLyR0xt3l8wMndNBOSgwrBJeM+TtJr16sGh9j0tNtNBOj+Fqi8ILPzZ
# lLufgGvC8P9hJ9lZCFuz5Iq+FJ9YxuvrY/3obqoBclDzwoepG575OPG4rIv8z4wF
# ZI+6hK6+30mCAgsyeLOGFDVVR4HaBZ9sW1u2XwU90APc34avbuBMV261Q+a8V0kx
# RFINoM0KvSR8bHR8jH3AiixUDlzg0s3GHi6Qnfq+hnKAwU6Mj3SnHIwupgKuvv4Y
# jA56bylCB6cmBM1lHmrbcil7TH/kKyXkPp8uYwS/sanTjjxM93YO6p/P6Ka1K1Gx
# iwb/X1ptfH1l3QL9TwrSy/2nDC7R6MnklTCkGKgx290JtLkPjUNGBOJFMUkGvlv3
# As2ACN9rjdJ2/TODK9aY+GyWYnTZDOCcmKns6ISC8WJIB226CUCEq0Fq5uUWHcoS
# gaabSfSSZ9Gety43Z85TlMY5bAG0Iq9lps/ziRO9NwWx5kGUcMz8db7u0ucwggee
# 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+fL4WrxSK5nMYIXMjCCFy4CAQEwcTBaMQsw
# CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYD
# VQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENBIDAyAhMzAAfM8rDv
# Jyd7QZv4AAAAB8zyMA0GCWCGSAFlAwQCAQUAoF4wEAYKKwYBBAGCNwIBDDECMAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJKoZIhvcNAQkEMSIEIF+wOaZ7
# io5/0IyanhWkcSysM7lV51IeaShZFn5T/Qf7MA0GCSqGSIb3DQEBAQUABIIBgDMa
# vnTCQk2D/PcAOVknR3fpC+7w5GZGh5J5jZ9bMsIwrDb+CbJkt1DCuFpc8ePQtI/C
# PTII2OX9R1PQ06kPbTNQBuSApGyZBzUQc+6tcYot93xoA8LP6SrggLyaYglQNpAi
# gOp2qFpuWnZUtNuQhq8543hJw6iS7hdc25BB8k1T3d/n53HUvzhNFjREEQwmkBFT
# WmUR3kfa0MiuKWB4ZQ+T5R645z3rKRte6i4GOOW2hKYs1o1Ul0EGU1QY2YlOnCZf
# FSS4AdtmqY71h+7Lxtj24Z2EXTXZDICTqSsYv9uVXvqKk+I9V/jGBvIclJ2UwDa5
# +y1i3FA5L/VJ7J2CwpB86TdgMuZPOAh9z7PZXmYAzOT7vtfOp3rnKduZngynjcEt
# ml99c1bCRLe/nt/vhVt2Z4HtXjsdyWCeoteYkxsVvYw2uk6qMpwtuMRWAmw9U389
# OlkwFeondZVjID8NDlMMN94XD1S8GTsuwV4flFwyIUngnE1nRjs5Gy6Iyh/yzqGC
# FLIwghSuBgorBgEEAYI3AwMBMYIUnjCCFJoGCSqGSIb3DQEHAqCCFIswghSHAgED
# MQ8wDQYJYIZIAWUDBAIBBQAwggFqBgsqhkiG9w0BCRABBKCCAVkEggFVMIIBUQIB
# AQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCAppQo946siIrK36g1F8Kyn
# bqD07iaNPJW6cOG7Xk8gtAIGacZcYMb4GBMyMDI2MDQwMzE0MzIyNi42MjdaMASA
# AgH0oIHppIHmMIHjMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQx
# JzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo0NTFBLTA1RTAtRDk0NzE1MDMGA1UE
# AxMsTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmg
# gg8pMIIHgjCCBWqgAwIBAgITMwAAAAXlzw//Zi7JhwAAAAAABTANBgkqhkiG9w0B
# AQwFADB3MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMUgwRgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJv
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAxMTE5MjAzMjMxWhcN
# MzUxMTE5MjA0MjMxWjBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1l
# c3RhbXBpbmcgQ0EgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AJ5851Jj/eDFnwV9Y7UGIqMcHtfnlzPREwW9ZUZHd5HBXXBvf7KrQ5cMSqFSHGqg
# 2/qJhYqOQxwuEQXG8kB41wsDJP5d0zmLYKAY8Zxv3lYkuLDsfMuIEqvGYOPURAH+
# Ybl4SJEESnt0MbPEoKdNihwM5xGv0rGofJ1qOYSTNcc55EbBT7uq3wx3mXhtVmtc
# CEr5ZKTkKKE1CxZvNPWdGWJUPC6e4uRfWHIhZcgCsJ+sozf5EeH5KrlFnxpjKKTa
# vwfFP6XaGZGWUG8TZaiTogRoAlqcevbiqioUz1Yt4FRK53P6ovnUfANjIgM9JDdJ
# 4e0qiDRm5sOTiEQtBLGd9Vhd1MadxoGcHrRCsS5rO9yhv2fjJHrmlQ0EIXmp4DhD
# BieKUGR+eZ4CNE3ctW4uvSDQVeSp9h1SaPV8UWEfyTxgGjOsRpeexIveR1MPTVf7
# gt8hY64XNPO6iyUGsEgt8c2PxF87E+CO7A28TpjNq5eLiiunhKbq0XbjkNoU5Jht
# YUrlmAbpxRjb9tSreDdtACpm3rkpxp7AQndnI0Shu/fk1/rE3oWsDqMX3jjv40e8
# KN5YsJBnczyWB4JyeeFMW3JBfdeAKhzohFe8U5w9WuvcP1E8cIxLoKSDzCCBOu0h
# WdjzKNu8Y5SwB1lt5dQhABYyzR3dxEO/T1K/BVF3rV69AgMBAAGjggIbMIICFzAO
# BgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGtpKDo1
# L0hjQM972K9J6T7ZPdshMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcn
# z1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRp
# b24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCB
# lAYIKwYBBQUHAQEEgYcwgYQwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVy
# aWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAy
# MC5jcnQwDQYJKoZIhvcNAQEMBQADggIBAF+Idsd+bbVaFXXnTHho+k7h2ESZJRWl
# uLE0Oa/pO+4ge/XEizXvhs0Y7+KVYyb4nHlugBesnFqBGEdC2IWmtKMyS1OWIviw
# pnK3aL5JedwzbeBF7POyg6IGG/XhhJ3UqWeWTO+Czb1c2NP5zyEh89F72u9UIw+I
# fvM9lzDmc2O2END7MPnrcjWdQnrLn1Ntday7JSyrDvBdmgbNnCKNZPmhzoa8PccO
# iQljjTW6GePe5sGFuRHzdFt8y+bN2neF7Zu8hTO1I64XNGqst8S+w+RUdie8fXC1
# jKu3m9KGIqF4aldrYBamyh3g4nJPj/LR2CBaLyD+2BuGZCVmoNR/dSpRCxlot0i7
# 9dKOChmoONqbMI8m04uLaEHAv4qwKHQ1vBzbV/nG89LDKbRSSvijmwJwxRxLLpMQ
# /u4xXxFfR4f/gksSkbJp7oqLwliDm/h+w0aJ/U5ccnYhYb7vPKNMN+SZDWycU5OD
# IRfyoGl59BsXR/HpRGtiJquOYGmvA/pk5vC1lcnbeMrcWD/26ozePQ/TWfNXKBOm
# kFpvPE8CH+EeGGWzqTCjdAsno2jzTeNSxlx3glDGJgcdz5D/AAxw9Sdgq/+rY7jj
# gs7X6fqPTXPmaCAJKVHAP19oEjJIBwD1LyHbaEgBxFCogYSOiUIr0Xqcr1nJfiWG
# 2GwYe6ZoAF1bMIIHnzCCBYegAwIBAgITMwAAAFxhTACmp/LpIwAAAAAAXDANBgkq
# hkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3Rh
# bXBpbmcgQ0EgMjAyMDAeFw0yNjAxMDgxODU5MDZaFw0yNzAxMDcxODU5MDZaMIHj
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRN
# aWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5T
# aGllbGQgVFNTIEVTTjo0NTFBLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0
# IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHkwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQCuNYzOVXmTR3eDbVZVMTaDh6n+nv04Jw4zW6v7
# OTN1oTSxBIhyq6QKMBanA2eXpOyqQ0VvcXgcC5Sz3aMgldXyKR79bMIeLVPQtXSC
# sjrrLjd9mpdd5VQq4HfKgaisPLFv6ynp1x2g1veINh5V+bLEFp1XCqWWCEd1KRMQ
# pkUkLSFRBZaFmb6Nz3MGrRAmU4nxt0wIV/20wSlzhaFKalpP0hSwI+1sVCxN8H8r
# lgh+0jGNZgCJEnXKw3SO/wJEjRhZMPaUSGEX7cwCrzqj/y1hnA+a8GbwfZ0lCQPm
# Mrjbcvx3j1VL6VecFnpdhfyP5ApLH5CSkXtNYhiFi7mG889+Miz+Ccr7awPiFP1L
# vAR9CTDZHz4ua/A5r4jOehH9AU7eEPxulAwy0BBywOl7tpK3gKMexwQygHFpZe5d
# 7TPiuVzKwktbh9ro4CkDwcpdUHCRgysNOuZr8Wd60f9DLkiooQFRh/micZx82DUT
# DlM5Ctcl4Ns4HejZNRGr+Gvd7V9kKuZF6XmW2td9qIZGL4eb515I0FUPRycH3vif
# tPZVKYDbkh8G/4I84tmfPh4PacHu6JCdkC0BsOU0EZMP8Su2h9NmgwVzk86NVH7f
# 1JhFcWwsxZxhRgI3K9EcH5xhWiMsqxebmZkrFighdqO7+yzozZZ3lkkqBWikYTg7
# ikY8PwIDAQABo4IByzCCAccwHQYDVR0OBBYEFK77MhyRGGREOW504FoDEie6JOdp
# MB8GA1UdIwQYMBaAFGtpKDo1L0hjQM972K9J6T7ZPdshMGwGA1UdHwRlMGMwYaBf
# oF2GW2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29m
# dCUyMFB1YmxpYyUyMFJTQSUyMFRpbWVzdGFtcGluZyUyMENBJTIwMjAyMC5jcmww
# eQYIKwYBBQUHAQEEbTBrMGkGCCsGAQUFBzAChl1odHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFB1YmxpYyUyMFJTQSUyMFRp
# bWVzdGFtcGluZyUyMENBJTIwMjAyMC5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwZgYDVR0gBF8wXTBRBgwr
# BgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAEEAjANBgkqhkiG
# 9w0BAQwFAAOCAgEAhXbQN9yxJIQFnYCFueVeZ09WRv1WkODNsgQQ7iClH9tHz1tD
# 6pbi0LT3KHa1xmQ3gojjdzqd5FE1wGMAaZvu2DzKyW0ZWYDED7BbW0nRwvWxE9DS
# VfKFwxhFgTkHu5cMB9Ofj2te8Qdv5plXp1lra2V9dSPAaBY3D9g8GUieV9Ruk03u
# hmCZffR+fIobO07/0xGT+3PJo+nlXjgrIsihl6UU2CS/cpbbenCinHJnK2lUozE7
# +N25qSyy9Vgqvsv09xxLY14syl0aOZt0xOvVIIqLKmeui3h9HbxQfbtfgexLQ40z
# 7wsnOJ/N/lt75vQaswZr39xEoMLeCeShyvgqMionmkQXT961Ti6LpNzGhZABR7YV
# pX+q84f9A6Kbl8amG8txKLdXOd9DKkCd9b+9sSTEnuULPsyuearCoNtr3JOm9psI
# Q/2TYeumHgbFWNpEvWoZBjLUfjoD89A5w4vjnketG8ImRyYn8MhzE3Brk/aVJ6e5
# wrentLyIYbZidbZRCDLq1uEmLVColC8z+ki3avMrNfgVKIQobf7YIvom1n00p6O7
# 3gQu8WiYuibJKgPxPD3RM1Dlh2m8GIkh15WTG6Gzk0dA5NyM2qoFFF/hNsbH5lY9
# qLrYb1meeXNfz3yytaeMs0Xvfq2N1eDYtDkRlEFoXn4JDWLKQdY2Ylw6vIMxggPU
# MIID0AIBATB4MGExCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFt
# cGluZyBDQSAyMDIwAhMzAAAAXGFMAKan8ukjAAAAAABcMA0GCWCGSAFlAwQCAQUA
# oIIBLTAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIE
# IKy6oA+6Cad5DT8GT1Rn2SX4+Hwyd5Ja7AG72jH6axftMIHdBgsqhkiG9w0BCRAC
# LzGBzTCByjCBxzCBoAQg97pqQYd3bjD5LeiLZCbTs0cAUbdUPCOdd6vJa36g7tww
# fDBlpGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5n
# IENBIDIwMjACEzMAAABcYUwApqfy6SMAAAAAAFwwIgQgGQuCwkvLVvw2lkXkhN/p
# i290+nUKte06wfuwlK+w8BowDQYJKoZIhvcNAQELBQAEggIATjzgsad8sZCl7Gsn
# Jytby30Wi3vX/GxlSz4VS+6jHaHQnda5irESi1Xyn7UTWR3lPzz8Lvu5F599s9HL
# ULucBSuBFprBPXQYdYqOO0AEOZsd+t1NdoeVz9jYiJXBLKG1nXnqd/jIy1AdwzXk
# 7/y20QrU+e8Oq4ppwNf1Im5CF/8xqa/yLJwAC37djA1cvY7KWUu8ubjnb6eAbvRm
# 1ltdwg++zQH/pS8Ewrop9NZTpVWfOPUtfZ1JraglaI6KgVm5rPjzhnoRJP0ExwV5
# XwedJ4S9Gv3JfZ/cX4A+tmJYbw1leh5UeUoDQkJNWfXKwRVmq5tT6ToaYKBCvt3V
# BCAxPfmPTUDBZ/XwIZOfARU8DF+qOXz4kpYDEexjWSBwJq7U/0qyDo0e95j8vOwL
# oEr8GV3+ko//tsq7rYCauUS9KbDBplqJ20Khxqb/ruP55SnPakNJPffLfYP01h2M
# /SupLj9mzKkYAt9iUHSRlHup58lvDaUANa8hMM/mFdR+qLB8v9hSLVmPr4x6hLFy
# 7KhKM5G/yatNq2Hb8nfzVYt1cJs3ep1GN3EBu2K8/DIXx2JRsT6cKUMVfbrTFccV
# zR3qaagTyziYGLhWwzjsufOQ0ARRcTZhZPNRU8T1tdr0ZjPY2Qq8F9+HG/r31Kud
# cHpvlX2njAEnopgL5Ok1axbxHUs=
# SIG # End signature block