Get-Permutation.ps1

<#PSScriptInfo
.Version 0.1.0
.Guid 19631007-5360-4a0e-86d4-e90ea9a08246
.Author Ronald Bode (iRon)
.CompanyName
.Copyright
.Tags Permutation Combination Collection Enumerate
.License https://github.com/iRon7/Get-Permutation/LICENSE.txt
.ProjectUri https://github.com/iRon7/Get-Permutation
.Icon https://raw.githubusercontent.com/iRon7/Get-Permutation/master/Get-Permutation.png
.ExternalModuleDependencies
.RequiredScripts
.ExternalScriptDependencies
.ReleaseNotes
.PrivateData
#>


<#
.SYNOPSIS
Generate permutations of a set.
 
.DESCRIPTION
This function enumerates all possible permutations of a given set.
 
.EXAMPLE
# Get all permutations of an given set
 
    Get-Permutation -InputObject 'Alpha', 'Beta', 'Gamma' | ForEach-Object { "$_" }
 
    Alpha Beta Gamma
    Alpha Gamma Beta
    Beta Alpha Gamma
    Beta Gamma Alpha
    Gamma Alpha Beta
    Gamma Beta Alpha
 
.EXAMPLE
# Get the first 5 permutations of a collection of 5 numbers
 
    1..5 | Get-Permutation | ForEach-Object { "$_" } | Select-Object -First 5
 
    1 2 3 4 5
    1 2 3 5 4
    1 2 4 3 5
    1 2 4 5 3
    1 2 5 3 4
 
.PARAMETER InputObject
 
The input collection to generate permutations for.
 
.LINK
[1]: https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order "wikipedia:Permutation#Generation_in_lexicographic_order"
[2]: https://gist.github.com/Jaykul/dfc355598e0f233c8c7f288295f7bb56 "Joel Bennett's how to implement `IEnumerator<T>` in PowerShell"
#>


using namespace System.Collections
using namespace System.Collections.Generic

param(
    [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [ValidateNotNullOrEmpty()]
    [string[]]$InputObject
)

Class Permutations : IEnumerator {
    [ICollection] $c    # Collection
    [int[]] $a          # Indices
    [int] $i            # Iteration

    Permutations([int]$Size) {
        $this.a = [int[]](0..($Size - 1))
    }

    Permutations([ICollection]$Collection) {
        $this.a = [int[]](0..($Collection.Count - 1))
        $this.c = $Collection
    }

    [object]get_Current() {
        if ($null -eq $this.c) { return $this.a }
        else { return $this.a.foreach{ $this.c[$_] } }
    }

    [bool] MoveNext() {
        # use the initial permutation on the first call to MoveNext
        if (-not $this.i++) { return $true }
        if ($this.a.Count -le 1) { $this.Reset(); return $false }
        # Find the largest index k such that a[k] < a[k + 1]. If no such index exists, the permutation is the last permutation.
        for ($k = $this.a.Count - 2; $this.a[$k] -gt $this.a[$k + 1]; $k--) { if (-not $k) { $this.Reset(); return $false } }
        # Find the largest index l greater than k such that a[k] < a[l].
        for ($l = $this.a.Count - 1; $l -ge 0; $l--) { if ($this.a[$k] -lt $this.a[$l]) { break } }
        # Swap the value of a[k] with that of a[l].
        $this.a[$k], $this.a[$l] = $this.a[$l], $this.a[$k]
        # Reverse the sequence from a[k + 1] up to and including the final element a[n].
        [Array]::Reverse($this.a, ($k + 1), ($this.a.Count - $k - 1))
        return $true
    }

    [void] Reset() {
        $this.a = [int[]](0..($this.a.Count - 1))
        $this.i = 0
    }

    [void] Dispose() {}
}

class Permutation : Permutations, IEnumerator[int] {
    Permutation($SizeOrSet) : base($SizeOrSet) { }
    [Int]get_Current() { return $this }
}

if ($Input) { [Permutation]$Input } else { [Permutation]$InputObject }