Git.psm1

function Add-GitTag {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $Version
    )

    $tagName = "v$Version"
    $sha = Start-NativeExecution git rev-parse HEAD
    $shortSha = $shortSha = $sha.Substring(0, 8)
    $commitMessage = "Added tag $tagName for changeset $shortSha"

    Log info $commitMessage
    Start-NativeExecution git tag -a $tagName -m $commitMessage
}

function Clear-GitBranches {
    [CmdletBinding()]
    param(
        [switch] $IncludeGoneRemoteBranches
    )

    $currentBranch = Start-NativeExecution git rev-parse --abbrev-ref HEAD
    if ($LASTEXITCODE -ne 0) {
        Log warning "Error on getting the current branch. Are you outside of a Git repository?"
        Log trace "$currentBranch"
        return
    }

    Log info "Searching for branches which already merged into '$currentBranch'..."

    $branches = @{ }

    # 1. Try the default way (no remote rebased branches can be detected)
    $mergedBranches = Get-GitMergedBranches
    $mergedBranches | ForEach-Object { Log trace "Branch found with Git default logic: '$_'" }
    if ($mergedBranches) {
        $branches.Add('default logic', $mergedBranches) > $null
    }

    # 2. Try the custom way (check each hash, also remote rebased branches can be detected)
    $mergedOrRebasedBranches = Get-GitMergedOrRebasedBranches
    $mergedOrRebasedBranches | ForEach-Object { Log trace "Branch found with Git deeper logic: '$_'" }
    if ($mergedOrRebasedBranches) {
        $branches.Add('deep logic', $mergedOrRebasedBranches) > $null
    }

    if ($IncludeGoneRemoteBranches) {
        # 3. Get also remote gone branches
        $remoteGoneBranches = Get-GitRemoteGoneBranches
        $remoteGoneBranches | ForEach-Object { Log trace "Branch found with Git remote gone logic: '$_'" }
        if ($remoteGoneBranches) {
            $branches.Add('gone logic', $remoteGoneBranches) > $null
        }
    }

    # Filter out duplicates
    $uniqueMergedBranches = Get-Distincted @($mergedBranches, $mergedOrRebasedBranches, $remoteGoneBranches) | Sort-Object
    if (-not $uniqueMergedBranches) {
        Log info 'No merged branches were found'
        return
    }

    $sb = [System.Text.StringBuilder]::new()
    $prefix = 'Merged branch:'
    $logicColumn = ($uniqueMergedBranches | Sort-Object Length -Descending | Select-Object -First 1).Length + 2
    foreach ($branch in $uniqueMergedBranches) {
        $logics = Get-Logics -groupedBranches $branches -branchName $branch

        $sb.Clear() > $null
        $sb.Append(("$prefix {0,-$logicColumn} {1}" -f "'$($branch)'", "[detected by: $($logics -join ', ')]")) > $null

        Log info $sb.ToString()
    }

    $title = 'Delete merged branches'
    $message = 'Do you want to delete the already-merged local branches displayed above?'
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Delete the remote branches listed.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Leave the branches alone.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $result = $Host.UI.PromptForChoice($title, $message, $options, 1)
    if ($result -eq 1) {
        return
    }

    $uniqueMergedBranches | ForEach-Object { Start-NativeExecution git branch -D $_ | Log info }
}

function Get-GitMergedBranches() {
    $mergedBranches = [System.Collections.ArrayList]@()
    $branches = Start-NativeExecution git branch --merged
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting merged branches: $branches"
        return
    }

    $filteredBranches = $branches | Where-Object { $_ -notmatch '^\* master$' } | Where-Object { $_ -notmatch '^master$' } | Where-Object { $_ -notmatch '^\* *$' } | ForEach-Object { $_.Trim() }

    foreach ($branch in $filteredBranches) {
        Log trace "Branch already merged '$branch'"
        $mergedBranches.Add($branch) > $null
    }

    return $mergedBranches
}

function Get-GitMergedOrRebasedBranches() {
    $currentBranch = Start-NativeExecution git rev-parse --abbrev-ref HEAD
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting merged or rebased branches: $currentBranch"
        return
    }

    Log trace "currentBranch: '$currentBranch'"

    $currentLocalBranch = Start-NativeExecution git symbolic-ref -q HEAD
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting merged or rebased branches: $currentLocalBranch"
        return
    }

    Log trace "currentLocalBranch: '$currentLocalBranch'"

    $currentRemoteBranch = Start-NativeExecution git for-each-ref '--format="%(upstream:short)"' $currentLocalBranch
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting merged or rebased branches: $currentRemoteBranch"
        return
    }

    Log trace "currentRemoteBranch: '$currentRemoteBranch'"

    $mergedbranches = [System.Collections.ArrayList]@()

    $localBranches = Start-NativeExecution git for-each-ref refs/heads '--format="%(refname:short)"' | Where-Object { $_ -notmatch 'master' } | Where-Object { $_ -notmatch $currentBranch }
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting merged or rebased branches: $localBranches"
        return
    }

    foreach ($localBranch in $localBranches) {
        $notMergedHashFound = $false
        Log trace "Check local branch '$localBranch'..."
        $results = Start-NativeExecution git cherry $currentRemoteBranch $localBranch
        if ($LASTEXITCODE -ne 0) {
            Log trace "Get merged hashes failed: '$results'"
            break
        }

        foreach ($result in $results) {
            if ($result -match '^\-') {
                Log trace "Hash merged '$result'"
                continue
            }

            if ($result -match '^\+') {
                Log trace "Hash was not merged '$result'"
            } else {
                Log trace "No hash '$result'"
            }

            $notMergedHashFound = $true
            break
        }

        if (-not $notMergedHashFound) {
            Log trace "Branch already merged '$localBranch'"
            $mergedbranches.Add($localBranch) > $null
        }
    }

    return $mergedbranches
}

function Get-GitRemoteGoneBranches() {
    $goneBranches = [System.Collections.ArrayList]@()
    $branches = Start-NativeExecution git branch -v
    if ($LASTEXITCODE -ne 0) {
        Log trace "Error on getting remote gone branches: $branches"
        return
    }

    $filteredBranches = $branches | Where-Object { $_ -notmatch '\* master' } | Where-Object { $_ -notmatch 'master' } | Where-Object { $_ -notmatch '\* *' } | Where-Object { $_ -match '^.*\[gone\].*' } | ForEach-Object { $_.Trim() }
    if (-not $filteredBranches) {
        Log trace "No remote gone branches found"
        return
    }

    $filteredBranchNames = $filteredBranches | ForEach-Object { $_.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[0] }
    foreach ($branchName in $filteredBranchNames) {
        Log trace "Branch is removed on remote '$branchName'"
        $goneBranches.Add($branchName) > $null
    }

    return $goneBranches
}

function Get-Distincted {
    param(
        [Parameter()]
        [object[][]] $Items
    )

    $hashSet = [System.Collections.Generic.HashSet[System.Object]]::new()
    foreach ($item in $Items) {
        foreach ($entry in $item) {
            $hashSet.Add($entry) > $null
        }
    }

    $distinctedList = [System.Collections.ArrayList]@()
    foreach ($value in $hashSet) {
        $distinctedList.Add($value) > $null
    }

    return $distinctedList
}

function Get-Logics([hashtable] $groupedBranches, [string] $branchName) {
    $logics = [System.Collections.ArrayList]@()

    foreach ($logic in $groupedBranches.GetEnumerator()) {
        [object[]] $values = $logic.Value
        if ($values.Contains($branchName)) {
            $logics.Add($logic.Name) > $null
        }
    }

    return $logics
}

function Invoke-GitkAll() {
    Invoke-Gitk -All @args
}

function Invoke-Gitk() {
    param(
        [switch] $All
    )

    $command = 'gitk'
    $arguments = [System.Collections.ArrayList]@()

    if ($All) {
        $arguments.Add('--all') > $null
    }

    $arguments += $args
    Start-NativeExecution $command @arguments
}

function Merge-GitAllRemoteBranches($RemoteRefs = $null, $Strategie = 'recursive', [switch] $UseTheirs = $false, [switch] $UseOurs = $false) {
    if ($null -eq $RemoteRefs) {
        $RemoteRefs = 'origin/'
    }

    $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim()

    git fetch --all
    git branch -r | ForEach-Object {
        $localBranch = $_
        $localBranch = $localBranch.Trim().Replace($RemoteRefs, '').Trim()
        $localBranch = $localBranch.Trim().Replace('*', '').Trim()
        $remoteBranch = $_.Trim().Replace('*', '').Trim()
        if ($remoteBranch.StartsWith($RemoteRefs) -and -not $remoteBranch.StartsWith($RemoteRefs + 'HEAD')) {
            Log info "Processing branch '${localBranch}'..."
            git checkout $localBranch

            if ($UseTheirs) {
                git merge $remoteBranch -s $Strategie -X theirs
            } elseif ($UseOurs) {
                git merge $remoteBranch -s $Strategie -X ours
            } else {
                git merge $remoteBranch
            }
        }
    }

    git checkout $currentBranch
}

function Merge-GitBranchUseTheirs($SourceBranchName, $DestinationBranchName) {
    if (($null -eq $SourceBranchName) -or ($null -eq $DestinationBranchName)) {
        return
    }

    $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim()
    $tempHash = Get-Checksum -Value ([System.Guid]::NewGuid()) -Algorithm SHA1
    $tempBranchName = "merge-use-theirs-${tempHash}"
    $commitMessage = "Merge branch '${SourceBranchName}' into '${DestinationBranchName}'"

    git checkout -b $tempBranchName $SourceBranchName
    git merge --strategy=ours $DestinationBranchName
    git checkout $DestinationBranchName
    git merge --no-ff $tempBranchName -m $commitMessage
    git branch -D $tempBranchName

    git checkout $currentBranch
}

function Push-GitAllTrackedBranches($RemoteRefs) {
    if ($null -eq $RemoteRefs) {
        $RemoteRefs = 'origin/'
    }

    $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim()

    git branch | ForEach-Object {
        $localBranch = $_
        $localBranch = $localBranch.Trim().Replace('*', '').Trim()

        $trackingRefs = git config branch.$localBranch.merge
        if ($trackingRefs -and ($trackingRefs.Trim().StartsWith('refs/heads/' + $RemoteRefs))) {
            Log info "Processing branch '${localBranch}'..."
            git checkout $localBranch
            git push
        }
    }

    git checkout $currentBranch
}

function Remove-GitAllBranches($RemoteRefs, [switch] $ForceAll = $false) {
    if ($null -eq $RemoteRefs) {
        $RemoteRefs = 'origin/'
    }

    $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim()
    $tempHash = Get-Checksum -Value ([System.Guid]::NewGuid()) -Algorithm SHA1
    $tempBranchName = "remove-all-${tempHash}"

    git checkout -f -b $tempBranchName

    git branch | ForEach-Object {
        $localBranch = $_
        $localBranch = $localBranch.Trim().Replace('*', '').Trim()

        $trackingRefs = git config branch.$localBranch.merge
        if ($trackingRefs -and ($localBranch -ne $tempBranchName) -and (($ForceAll) -or ($null -ne $trackingRefs))) {
            if (($ForceAll) -or
                ($trackingRefs.Trim().StartsWith('refs/remotes/' + $RemoteRefs)) -or
                ($trackingRefs.Trim().StartsWith('refs/heads/' + $RemoteRefs))) {
                Log info "Delete local branch '${localBranch}'..."
                git branch -D $localBranch
            }
        }
    }

    if ($ForceAll) {
        git checkout master
    } else {
        git checkout $currentBranch
    }

    git branch -D $tempBranchName
}

function Reset-GitAllBranches($RemoteRefs) {
    Remove-GitAllBranches $RemoteRefs
    Set-GitTrackAllRemoteBranches $RemoteRefs
    Merge-GitAllRemoteBranches $RemoteRefs
}

function Set-GitTrackAllRemoteBranches($RemoteRefs) {
    if ($null -eq $RemoteRefs) {
        $RemoteRefs = 'origin/'
    }

    $localBranches = git branch | ForEach-Object { $_.Trim().Replace('*', '').Trim() }

    $notTrackedLocalBranches = @()
    git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads' | ForEach-Object {
        $row = $_ -split '<-'
        $localBranch = $row[0].Trim()
        $remoteBranch = $row[1].Trim()

        $query = "branch.${localBranch}.remote"
        $configRemote = git config --get $query
        if (-not $remoteBranch -and -not $configRemote) {
            $notTrackedLocalBranches += $localBranch
        }
    }

    git branch -r | ForEach-Object {
        $localBranch = $_
        $localBranch = $localBranch.Trim().Replace($remoteRefs, '').Trim()
        $localBranch = $localBranch.Trim().Replace('*', '').Trim()
        $remoteBranch = $_.Trim().Replace('*', '').Trim()

        if ($remoteBranch.StartsWith($remoteRefs + 'HEAD') -or -not ($remoteBranch.StartsWith($remoteRefs))) {
            return
        }

        if (-not ($notTrackedLocalBranches -contains $localBranch) -and -not ($localBranches -contains $localBranch)) {
            git branch -t $localBranch $remoteBranch
        } elseif ($notTrackedLocalBranches -contains $localBranch) {
            git branch -u $remoteBranch $localBranch
        }
    }
}

function Set-GitTrackMatchedRemoteBranches($RemoteRefs) {
    if ($null -eq $RemoteRefs) {
        $RemoteRefs = 'origin/'
    }

    $notTrackedLocalBranches = @()

    git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads' | ForEach-Object {
        $row = $_ -split '<-'
        $localBranch = $row[0].Trim()
        $remoteBranch = $row[1].Trim()

        $query = "branch.${localBranch}.remote"
        $configRemote = git config --get $query
        if (-not $remoteBranch -and -not $configRemote) {
            $notTrackedLocalBranches += $localBranch
        }
    }

    $remotes = git branch -r | ForEach-Object { $_.Trim().Replace('*', '').Trim() }

    $notTrackedLocalBranches | ForEach-Object {
        $localBranch = $_
        $remotes | ForEach-Object {
            $remoteBranch = $_
            if ($remoteBranch -eq ($RemoteRefs + $localBranch)) {
                git branch -u $remoteBranch $localBranch
                return
            }
        }
    }
}

function Update-GitAllBranches() {
    $currentBranch = git rev-parse --abbrev-ref HEAD

    git fetch --all

    $branchPairs = git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads'
    $branchPairs | ForEach-Object {
        $pair = $_ -split ' <- '
        $localBranch = $pair[0]
        $remoteBranch = $pair[1]
        if ($localBranch -and $remoteBranch) {
            Log info "Processing branch '${localBranch}'..."
            git checkout $localBranch
            git rebase $remoteBranch
        }
    }

    git checkout $currentBranch
}

function Update-GitBranch() {
    git fetch --all

    $localBranch = git rev-parse --abbrev-ref HEAD
    $remoteBranch = git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}'
    if (-not $remoteBranch) {
        Print-Warning "No upstream configured for branch '${localBranch}'"
        return
    }

    $remoteBranches = git for-each-ref --format='%(upstream:short)' 'refs/heads'
    foreach ($branch in $remoteBranches) {
        if ( $branch -eq $remoteBranch) {
            Log info "Rebase branch '${remoteBranch}' into '${localBranch}'..."
            git rebase $remoteBranch
            break
        }
    }
}

New-Alias agt Add-GitTag
New-Alias cgb Clear-GitBranches
New-Alias gk Invoke-Gitk
New-Alias gka Invoke-GitkAll
New-Alias pgst Push-GitAllTrackedBranches
New-Alias ugab Update-GitAllBranches
New-Alias ugb Update-GitBranch
# SIG # Begin signature block
# MIIcjgYJKoZIhvcNAQcCoIIcfzCCHHsCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBcBvPFGjaf0jQz
# wdePttaTyhY6rmVTgEzx42TrDkL9qqCCF5gwggUhMIIECaADAgECAhAIWwDz5iwy
# UtohxU7HR7bMMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcN
# MTkwMzEyMDAwMDAwWhcNMjAwMzE2MTIwMDAwWjBeMQswCQYDVQQGEwJERTEfMB0G
# A1UEBxMWR2FybWlzY2gtUGFydGVua2lyY2hlbjEWMBQGA1UEChMNTWFudWVsIFRh
# bnplcjEWMBQGA1UEAxMNTWFudWVsIFRhbnplcjCCASIwDQYJKoZIhvcNAQEBBQAD
# ggEPADCCAQoCggEBAPIHE2FyXkrHpGfPf80N/4sJPgORb+Br0+wCuJSD8BMRNB40
# 1Rmn2dcq7IEfvud6qCdnxo/jLmTWNiEb7dr+NcRvIggi5yUM48DjpUnYpsDAIVTQ
# 1j1+X7DgaCy7KrR9qsJciYvsZqjFOG7vHdOfU8LUaGD+eKHlL8uKOAdgHT7KnNJi
# QQtK1fK2D0MUK+CqBuFg4m+XDfQbugzi5w9YkbdlIaQoxjVWogDqG9fs60501ly6
# yDrS4oOQFnHWcx1HlWFrGPU6kMMdDLeVC211qbIMFH+z8Rc+aSWzXBlxn9TUygJW
# ghkNTGS/2C34RjtQDK/4rl2Koh7NHqCUMJf1bIECAwEAAaOCAcUwggHBMB8GA1Ud
# IwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBSjqX8VbvlnZ2WB
# n2oo/qfn1g674TAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
# dwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTIt
# YXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMB
# MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYG
# Z4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw
# LmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwG
# A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAOIRTvC2IfJXdRz46zRs+4+z
# YKsufAnKNlAkgMc4cX2NKD/u5kvB1aS1EFAE9vRLPURtDgiLia6I8nLOUag4iWpO
# mH9nd8utH+obJhR3l35jB8WP/RVdcRU9GicRT9ARWLZEby6CYpq081WNCtxoPCEs
# +bAiCBcR6KkWP5YUGoC0tBn5aeoTmpJgtLjGGjtsHQH9Xoak7T39gjbJZLoztVfE
# A78MSmjvTvVyn4SfgVT31y9puQxMwusrZf+axm51SJp0YTYVAuHtNVqfxve4QBXq
# 6OXtGnceVuEmcH9cSRYo5GOhuNyq4yIq1/yLIWeLiBxlqaKauSPPG8yCaXFs1LEw
# ggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9v
# dCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYT
# AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy
# dC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNp
# Z25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4R
# r2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrw
# nIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnC
# wlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8
# y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM
# 0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6f
# pjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud
# DwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGsw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcw
# AoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBP
# BgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93
# d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoK
# o6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8w
# DQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+
# C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119E
# efM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR
# 4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4v
# cn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwH
# gfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggZqMIIFUqADAgEC
# AhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEwMjIw
# MDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhE
# aWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRlcjCC
# ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq5kl1
# O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0iEQ7
# Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZbFP1F
# I46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3gTAf
# uzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8c3T6
# Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkRj3cC
# AwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgBhv1s
# BwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
# MIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABo
# AGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0
# AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBn
# AGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBs
# AHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABp
# AGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABh
# AHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABi
# AHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAW
# gBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnkag0v0
# HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0LmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUFBwEB
# BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG
# AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1
# cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuGziMg
# D4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+/iCk
# V61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgdKG86
# Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB2vkP
# gdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7ELlmg
# Yd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qNe9zY
# RDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEFBQAw
# ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS
# b290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZUXKnE
# 0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHrzzpA
# DEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LNb3Mj
# /qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQxl0S
# QoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao8S+v
# 7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQIDAQAB
# o4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMBBggr
# BgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1UdIASC
# AckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93
# d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggrBgEF
# BQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBl
# AHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBj
# AGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0
# ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAg
# AFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABp
# AG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBu
# AGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBm
# AGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8CAQAw
# eQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
# dC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j
# cmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLNMB8G
# A1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUAA4IB
# AQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXDUOSC
# uSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2QwsDS
# tZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JLFuRL
# cEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP+1ng
# Iw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3cdyx
# G0tLHBCcdxTBnU8vWpUIKRAmMYIETDCCBEgCAQEwgYYwcjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmlu
# ZyBDQQIQCFsA8+YsMlLaIcVOx0e2zDANBglghkgBZQMEAgEFAKCBhDAYBgorBgEE
# AYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCAc8rvb
# 5sDz1WTm5Ir+/IAPBcZji3WOQtKz2RFdHZKwjTANBgkqhkiG9w0BAQEFAASCAQAD
# iluzGC0Pd23jQqoYHoaW0vlgQk2zsYAURZ/LuZ4xmHG0uQJCpDRvTMTL/tgmx9Bz
# IoM/mmk20214vjAHrat6/Uyk7C9NuaKE6fSPBi3ZWg4hIgSz1nZX1rKXKGNXoI3l
# CSf9nkbYN3FnvwLsR2icR36kuvL6kmXdVwkMtL6z+zC2GP+9sdvsA2vIKYg5I/25
# ENNpEQitmFe9a8ARCYbz3OWsDgxB1pEKuOJbH73AnMOUbUEbtxZHfgwBhGcRzypK
# qFWXDhwNiolxKKIvV4+cBqcOSflgGHDQj+hwjplgltbp0Tmj+LMFNtRl22U4mYv2
# DRyNpm1sr9v6HwpUeJmkoYICDzCCAgsGCSqGSIb3DQEJBjGCAfwwggH4AgEBMHYw
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBD
# QS0xAhADAZoCOv9YsWvW1ermF/BmMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMx
# CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAzMDkyMTUzMTNaMCMGCSqG
# SIb3DQEJBDEWBBTA6GcR5oI4Tbr9P8RRyLoz85724jANBgkqhkiG9w0BAQEFAASC
# AQBICOvfFHDFRHbdn64E4y9XXTSpBQwOmch/83UvcHGBKkJE+yekyv3gPCEYtSA0
# mg+NNZI/aHiqLWHt5Vd+81ggDsQvzEvX6BOYXvjaClMIaoDSZUhBACPqXr77IY9J
# p9P0D8clXt00o+NrXSTT++GmClX2BB1eBUTLZC+6Dz/VCN1Tue79zohEiKuW9wxS
# VaHgYD2+c7Ph0lbkm6XsF9DIYR8KRjC3k4FX5ClxOQPsiocCsDG22M/yM2QyEPrd
# fAaRH5ThLVmgIqHYrFw2spZoPwV9WY9bTQNqTsrvJFcSaHvrSvSvdRMg9WYUtjry
# 2SRXtskkdZaW0KSqSIXMaC/9
# SIG # End signature block