Parsing binary data in PowerShell - powershell

I have read the contents of a binary file as an array of unsigned bytes but I want to interpret some of them as signed bytes. How do I do that?
#An array of unsigned bytes
[byte[]]$b = #(255)
#This doesn't work
[sbyte]$s = $b[0]
#Neither does this
[sbyte]$s = [convert]::ToSByte($b[0])
What I wanted was the value -1.

The \[System.Convert\]::ToSByte(value) method raises an OverflowException if value is greater than [sbyte]::MaxValue.
Return to theory (Converting from two's complement representation):
$mask = [byte] 0x80 # [math]::pow(2,7)
$b = [byte] 255
$s = [sbyte] ( -($b -band $mask) + ($b -band ( -bnot $mask )) )
'result: {0,4}, {1,4} ( = 0x{0:x2}, 0x{1:x2} )' -f $b, $s
Output:
result: 255, -1 (= 0xff, 0xff )
Of course, you could simplify the calculation as
$s = [sbyte] ( -($b -band 0x80) + ($b -band 0x7F) )
Edit to refute PetSerAl's doubts about [sbyte] -($b -band $mask) + ($b -band ( -bnot $mask )) formula correctness. PetSerAl's doubts are right: results in Powershell-ISE differ from those in Powershell! The formula should be (see labelled parentheses)
# ↓ ↓
[sbyte] ( -($b -band $mask) + ($b -band ( -bnot $mask )) )
# ↑ ↑
Proof:
function AsSignedByte { ### taken from PetSerAl's comment to the OQ
param([Byte]$Byte)
[sbyte]$( if($Byte -ge 128) { $Byte - 256 } else { $Byte })
}
$mask = [byte] 0x80 # [math]::pow(2,7)
$good = 0
For ($i = 0 ; $i -lt 256; $i++) {
$b = [byte] $i
$s = [sbyte] ( -($b -band $mask) + ($b -band ( -bnot $mask )) )
$ss = AsSignedByte $b
if ( ($s -ne $ss) -or
($s.GetTypeCode() -ne $ss.GetTypeCode()) -or
($s -isnot [sbyte])) {
'result: {0,4}, {1,4} ( = 0x{0:x2}, 0x{1:x2} )' -f $s, $ss
} else {
$good +=1
}
}
$good
Output:
PS D:\PShell> D:\PShell\SO\48085510.ps1
256
PS D:\PShell>

Related

How to convert roman numbers into decimal in PowerShell?

I want to convert Roman Numbers into Decimal Numbers Like:
"MCMVII" = 1907;
"MMCDXLVII" = 2447;
"MMCMLXXXIV" = 2984;
"MXCVI" = 1096;
"MCMIV" = 1904;
"MMDII" = 2502;
"M" = 1000;
"MMDLXXIX" = 2579;
"MMMLXXXVIII" = 3088;
"MMDCCXCIX" = 2799;
"MMDVI" = 2506;
"MMMDCCLVII" = 3757;
"MMMCCLXXXIII" = 3283;
"MCDXL" = 1440;
"MMD" = 2500;
"DCLI" = 651;
Can someone help me with this? I am struggling from where to start??
There are good hints in the comments, but neither bluuf's helpful recipe nor the implementations pointed to Lee_Dailey amount to robust implementations.
A robust implementation that rules out syntactically invalid Roman numerals is surprisingly complex - find function ConvertFrom-RomanNumeral in the bottom section.
Applied to your sample values, you'll get the corresponding decimal numbers as shown in your question:
#(
"MCMVII"
"MMCDXLVII"
"MMCMLXXXIV"
"MXCVI"
"MCMIV"
"MMDII"
"M"
"MMDLXXIX"
"MMMLXXXVIII"
"MMDCCXCIX"
"MMDVI"
"MMMDCCLVII"
"MMMCCLXXXIII"
"MCDXL"
"MMD"
"DCLI"
) | ConvertFrom-RomanNumeral
A helpful online resource is the Roman Numerals Converter, which offers conversion both to and from Roman numerals.
ConvertFrom-RomanNumeral source code:
Any syntactically invalid Roman numeral results in a non-terminating error - do let us know if I missed any cases.
No limit is placed on the number of M digits (each representing 1000) in a row.
function ConvertFrom-RomanNumeral {
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string] $RomanNumeral
)
begin {
function toValue {
param ([string] $RomanNumeral, [int] $i)
if ($i -ge $RomanNumeral.Length) { return $null }
$value = #{
[char] 'i' = 1
[char] 'v' = 5
[char] 'x' = 10
[char] 'l' = 50
[char] 'c' = 100
[char] 'd' = 500
[char] 'm' = 1000
}[[char]::ToLower($RomanNumeral[$i])]
if (-not $value) {
throw "Cannot convert '$RomanNumeral': not a Roman digit: '$($RomanNumeral[$i])'"
}
$value # output
}
}
process {
$num = 0; $maxDigitSoFar = 1001; $prevValue = 0
for ($i = 0; $i -lt $RomanNumeral.Length; ++$i) {
try {
$value = toValue $RomanNumeral $i
if ($isMultiDigitRun = $value -eq $prevValue) { # Check for too many digits in a row.
++$runCount
if ($value -ne 1000 -and $runCount -gt 3) { throw "Cannot convert '$RomanNumeral': no more than 3 digits other than M allowed in sequence." }
if ($value -in 5, 50, 500) { throw "Cannot convert '$RomanNumeral': V, L, and D cannot be repeated." }
} else {
$runCount = 1
}
if (($nextValue = toValue $RomanNumeral ($i + 1)) -gt $value) { # A subtraction, such as IV
$isValid = switch ($value) { # Verify that the next digit is valid; V, L, D cannot be used in subtractions.
1 { $nextValue -in 5, 10; break } # IV, IX
10 { $nextValue -in 50, 100; break } # XL, XC
100 { $nextValue -in 500, 1000; break } # CD, CM
}
if (-not $isValid) { throw "Cannot convert '$RomanNumeral': invalid subtraction sequence: '$($RomanNumeral[$i])$($RomanNumeral[$i+1])'" }
$isValid = $nextValue -le $maxDigitSoFar # Also check if the digit is too high.
++$i # Skip the next digit, given that we've just processed it.
$prevValue = 0 # Signal that the next digit is by definition not part of a run.
$maxDigitSoFar = $value # Signal that this digit must not be used again later, except in another subtraction.
$value = $nextValue - $value
}
else { # A stand-alone digit.
$isValid = $isMultiDigitRun -or $value -lt $maxDigitSoFar
$prevValue = $value
$maxDigitSoFar = $value
}
if (-not $isValid) { throw "Cannot convert '$RomanNumeral': digits not in expected order." }
$num += $value
}
catch {
Write-Error "$_"; return
}
}
$num # output
}
}
To complement the answer from #mklement0 and taking the complexity away by seeing each decimal number as a roman numeral pattern that represent the numbers 1 to 9. Note here that (as some other ancient number systems) roman numerals don't have/require zeros (M000 ➔ M equals 1000) as each roman number is unique to its own bound value:
function ConvertFrom-RomanNumeral {
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string] $RomanNumeral
)
begin {
$1 = 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix'
$10 = 'x', 'xx', 'xxx', 'xl', 'l', 'lx', 'lxx', 'lxxx', 'xc'
$100 = 'c', 'cc', 'ccc', 'cd', 'd', 'dc', 'dcc', 'dccc', 'cm'
$Pattern = "^(m*)($($100 -Join '|'))?($($10 -Join '|'))?($($1 -Join '|'))?$"
}
process {
if ($_ -Match $Pattern) {
$Value = if ($Matches[1]) { 1000 * $Matches[1].Length }
if ($Matches[2]) { $Value += 100 * $100.IndexOf($Matches[2].ToLower()) + 100 }
if ($Matches[3]) { $Value += 10 * $10.IndexOf($Matches[3].ToLower()) + 10 }
if ($Matches[4]) { $Value += $1.IndexOf($Matches[4].ToLower()) + 1 }
$Value
} else { throw "Cannot convert $_" }
}
}

Windows Powershell equivalent to this Linux command that calculates a number to 10000 decimal places?

I have seen this fun little command going around on social media, which outputs pi to 10000 decimal places. I'm wondering what is the Windows Powershell equivalent to this?
echo "scale=10000; 4*a(1)" | bc -l
I know I can use the Powershell Math library to do the basic math formula, but how to set the number of decimal places to 10000?
PS C:\Users\Me> [Math]::Atan(1)*4
3.14159265358979
The [Math] class only works with double so obviously you can't get more than 53 bits of precision. A few of its methods also support decimal so you'll get a little bit more digits but obviously no way near 10000 digits. There's no arbitrary precision floating point type in .NET so you're completely on your own to calculate the digits if you don't want to retrieve them from some storage space
There's BigInteger (PowerShell type accelerator name: [bigint]) though, which helps a lot since you won't need to do arbitrary precision math yourself. For example to calculate 10000 digits of π you can calculate 10000π and does the operations in integer or fixed point math
There are many algorithms to do that such as this one: How does this code calculate pi with high precision?
Fortunately Rosetta Code has a sample snippet to calculate π. After the below function has been declared just call Get-Pi 10000 to get the desired output
Function Get-Pi ( $Digits )
{
$Big = [bigint[]](0..10)
$ndigits = 0
$Output = ""
$q = $t = $k = $Big[1]
$r = $Big[0]
$l = $n = $Big[3]
# Calculate first digit
$nr = ( $Big[2] * $q + $r ) * $l
$nn = ( $q * ( $Big[7] * $k + $Big[2] ) + $r * $l ) / ( $t * $l )
$q *= $k
$t *= $l
$l += $Big[2]
$k = $k + $Big[1]
$n = $nn
$r = $nr
$Output += [string]$n + '.'
$ndigits++
$nr = $Big[10] * ( $r - $n * $t )
$n = ( ( $Big[10] * ( 3 * $q + $r ) ) / $t ) - 10 * $n
$q *= $Big[10]
$r = $nr
While ( $ndigits -lt $Digits )
{
While ( $ndigits % 100 -ne 0 -or -not $Output )
{
If ( $Big[4] * $q + $r - $t -lt $n * $t )
{
$Output += [string]$n
$ndigits++
$nr = $Big[10] * ( $r - $n * $t )
$n = ( ( $Big[10] * ( 3 * $q + $r ) ) / $t ) - 10 * $n
$q *= $Big[10]
$r = $nr
}
Else
{
$nr = ( $Big[2] * $q + $r ) * $l
$nn = ( $q * ( $Big[7] * $k + $Big[2] ) + $r * $l ) / ( $t * $l )
$q *= $k
$t *= $l
$l += $Big[2]
$k = $k + $Big[1]
$n = $nn
$r = $nr
}
}
$Output
$Output = ""
}
}

powershell Fibonacci sequence stops early

when i enter 55 i want the function to print the fibonacci sequence 55 times but the sequence stops at 55
function Get-Fibonacci ($n) {
$current = 0 ;
$previous = 1;
while ($current -lt $n) {
$current;
$current,$previous = ($current+$previous),$current
}
}
function Get-Fibonacci ($n) {
$current = 0 ;
$previous = 1;
for ($i=0; $i -lt $n; $i++) {
$current;
$current,$previous = ($current+$previous),$current
}
}
Here's an answer which doesn't rewrite using a for loop:
function Get-Fibonacci ($n) {
$current = 0 ;
$previous = 1;
$position=0
while ($position -lt $n) {
$position++
'{0} : {1}' -f $position,$current
$current,$previous = ($current+$previous),$current
}
}

Is there a way to center text in PowerShell

How can we center text in PowerShell? WindowWidth doesn't exist apparently, so is there a way somehow to keep the text centered?
We want this output :
*
***
*****
*******
So I wrote
for ($i=1; $i -le 7; $i+=2)
{
write-host("*" * $i)
}
But we get
*
***
*****
*******
function Write-HostCenter { param($Message) Write-Host ("{0}{1}" -f (' ' * (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Floor($Message.Length / 2)))), $Message) }
Write-HostCenter '*'
Write-HostCenter '***'
Write-HostCenter '*****'
I had a bit of fun and wrote some code based on this, that makes a box and center the text inside.
Im sure someone can make a cleaner version, but this do the job just fine :)
# ----------------------------------------------------------------------------------
#
# Script functions
#
# ----------------------------------------------------------------------------------
function MakeTopAndButtom
{
$string = "# "
for($i = 0; $i -lt $Host.UI.RawUI.BufferSize.Width - 4; $i++)
{
$string = $string + "-"
}
$string = $string + " #"
return $string
}
function MakeSpaces
{
$string = "# "
for($i = 0; $i -lt $Host.UI.RawUI.BufferSize.Width - 4; $i++)
{
$string = $string + " "
}
$string = $string + " #"
return $string
}
function CenterText
{
param($Message)
$string = "# "
for($i = 0; $i -lt (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Max(0, $Message.Length / 2))) - 4; $i++)
{
$string = $string + " "
}
$string = $string + $Message
for($i = 0; $i -lt ($Host.UI.RawUI.BufferSize.Width - ((([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Max(0, $Message.Length / 2))) - 2 + $Message.Length)) - 2; $i++)
{
$string = $string + " "
}
$string = $string + " #"
return $string
}
function LinesOfCodeInCorrentFolder
{
return (gci -include *.ps1 -recurse | select-string .).Count
}
$MakeTopAndButtom = MakeTopAndButtom
$MakeSpaces = MakeSpaces
$lines = LinesOfCodeInCorrentFolder
# ----------------------------------------------------------------------------------
#
# Run
#
# ----------------------------------------------------------------------------------
$MakeTopAndButtom
$MakeSpaces
$MakeSpaces
$MakeSpaces
$MakeSpaces
CenterText "Lines of .ps1 code in this folder: $($lines)"
CenterText "Press any key to exit"
$MakeSpaces
$MakeSpaces
$MakeSpaces
$MakeSpaces
$MakeTopAndButtom
Read-Host
This gives an output like this:
# ---------------------------------------------------------------------------------------- #
# #
# #
# #
# #
# Lines of .ps1 code in this folder: 6524 #
# Press any key to exit #
# #
# #
# #
# #
# ---------------------------------------------------------------------------------------- #
You can calculate the spaces you need to add and then include them as follows:
$Width = 3
for ($i=1; $i -le 7; $i+=2)
{
Write-Host (' ' * ($width - [math]::floor($i / 2))) ('*' * $i)
}

Powershell: HSL to RGB

I need to convert HSL color values to RGB, or to be more precise HSL-values to a System.Drawing.Color object with Powershell.
There are a few solutions in other prog.-languages out there (like LINK). But while it looks simple, I dont get it converted it into Powershell.
Function HSLtoRGB ($H,$S,$L) {
$H = [double]($H / 360)
$S = [double]($S / 100)
$L = [double]($L / 100)
if ($s -eq 0) {
$r = $g = $b = $l
}
else {
if ($l -lt 0.5){
$q = $l * (1 + $s)
}
else {
$q = $l + $s - $l * $s
}
$p = (2 * $L) - $q
$r = (Hue2rgb $p $q ($h + 1/3))
$g = (Hue2rgb $p $q $h )
$b = (Hue2rgb $p $q ($h - 1/3))
}
$r = [Math]::Round($r * 255)
$g = [Math]::Round($g * 255)
$b = [Math]::Round($b * 255)
return ($r,$g,$b)
}
function Hue2rgb ($p, $q, $t) {
if ($t -lt 0) { $t++ }
if ($t -gt 0) { $t-- }
if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) }
if ($t -lt 1/2) { return $q }
if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
return $p
}
HSLtoRGB 63 45 40 # result should be R 145 G 148 B 56
Let's start with the line you're having trouble with translating:
$q = l < 0.5 ? l * (1 + s) : l + s - l * s; #could not translate this line
This construct:
statement ? someValue : anotherValue;
is known as a ternary operation. It basically means:
if(statement){
someValue
} else {
anotherValue
}
So in PowerShell that becomes:
$q = if($l -lt 0.5){
$l * (1 + $s)
} else {
$l + $s - $l * $s
}
Your translation of the inline Hue2Rgb function has two typos that greatly change the calculation:
function Hue2rgb ($p, $q, $t) {
if ($t -lt 0) { $t++ }
if ($t -gt 0) { $t-- } # This condition should be ($t -gt 1)
if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) } # The innermost calculation should be ($q - $p) not ($q + $p)
if ($t -lt 1/2) { return $q }
if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
return $p
}
Regarding the input values, if you take a look at the comments in the original script:
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
So if you want to pass your input values as degrees (hue) and percentages (saturation + luminance), you'll have to handle a conversion to a relative value between 0 and 1:
Function HSLtoRGB ($H,$S,$L) {
$H = [double]($H / 360)
$S = [double]($S / 100)
$L = [double]($L / 100)
# rest of script
}
Lastly, you can use Color.FromArgb() to return an actual Color object:
$r = [Math]::Round($r * 255)
$g = [Math]::Round($g * 255)
$b = [Math]::Round($b * 255)
return [System.Drawing.Color]:FromArgb($r,$g,$b)