Utils/factor.ps1
|
Register-GnuUtil @{ Name = 'factor' Category = 'Shell helpers' Synopsis = 'Factor numbers.' Usage = @('factor [NUMBER]...', 'factor OPTION') Description = @( 'Print the prime factors of each specified integer NUMBER. If none', 'are specified on the command line, read them from standard input.' ) NegativeNumberOperands = $true Options = @( @{ Short = 'h'; Long = 'exponents'; Help = 'print repeated factors in form p^e unless e is 1' } ) Notes = @( 'Numbers of any size are accepted (System.Numerics.BigInteger, Miller-Rabin, Pollard rho).', 'PowerShell note: much slower than GNU; numbers whose two smallest prime factors both have', 'more than about 12 digits can take very long.' ) } function script:Test-GnuProbablePrime { <# .SYNOPSIS Miller-Rabin test; deterministic for numbers below 3.3e24 with these bases. #> param([Parameter(Mandatory)][System.Numerics.BigInteger]$Value) if ($Value -lt 2) { return $false } foreach ($small in @(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41)) { if ($Value -eq $small) { return $true } if ($Value % $small -eq 0) { return $false } } $d = $Value - 1 $r = 0 while ($d % 2 -eq 0) { $d = $d / 2; $r++ } foreach ($base in @(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41)) { $x = [System.Numerics.BigInteger]::ModPow($base, $d, $Value) if ($x -eq 1 -or $x -eq $Value - 1) { continue } $composite = $true for ($i = 1; $i -lt $r; $i++) { $x = [System.Numerics.BigInteger]::ModPow($x, 2, $Value) if ($x -eq $Value - 1) { $composite = $false; break } } if ($composite) { return $false } } $true } function script:Find-GnuFactorPollard { <# .SYNOPSIS Finds a non-trivial factor of a composite number with Pollard's rho (Floyd cycle detection). #> param([Parameter(Mandatory)][System.Numerics.BigInteger]$Value) for ($c = 1; ; $c++) { $x = [System.Numerics.BigInteger]2 $y = [System.Numerics.BigInteger]2 $d = [System.Numerics.BigInteger]::One while ($d -eq 1) { $x = ($x * $x + $c) % $Value $y = ($y * $y + $c) % $Value $y = ($y * $y + $c) % $Value $d = [System.Numerics.BigInteger]::GreatestCommonDivisor([System.Numerics.BigInteger]::Abs($x - $y), $Value) } if ($d -ne $Value) { return $d } } } function script:Get-GnuPrimeFactor { <# .SYNOPSIS The prime factors of a number in ascending order (with repetition). #> param([Parameter(Mandatory)][System.Numerics.BigInteger]$Value) $factors = New-Object 'System.Collections.Generic.List[System.Numerics.BigInteger]' $n = $Value foreach ($p in @(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)) { while ($n -gt 1 -and $n % $p -eq 0) { $factors.Add($p); $n = $n / $p } } $pending = New-Object 'System.Collections.Generic.Stack[System.Numerics.BigInteger]' if ($n -gt 1) { $pending.Push($n) } while ($pending.Count -gt 0) { $current = $pending.Pop() if (Test-GnuProbablePrime -Value $current) { $factors.Add($current); continue } $divisor = Find-GnuFactorPollard -Value $current $pending.Push($divisor) $pending.Push($current / $divisor) } $factors.Sort() foreach ($factor in $factors) { $factor } } |