How to convert roman numbers into decimal in PowerShell? - 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 $_" }
}
}

Related

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
}
}

It there a way in powershell to make a positive number to a negative number whitout using multiplication?

I was wondering if there is a way to make a positive number into a negative number whitout using a multiplication like $b = $a * -1
I'm looking for the most cost sensible way because I'm gonna do this a lot of times in a script.
-edit
At this point I'm using this, but lookes very costly computation wise:
$temp_array = New-Object 'object[,]' $this.row,$this.col
for ($i=0;$i -le $this.row -1 ; $i++) {
for ($j=0;$j -le $this.col -1 ; $j++) {
$digit = $this.data[$i,$j] * -1
$temp_array[$i,$j] = 1 / ( 1 + [math]::exp( $digit ) )
#[math]::Round( $digit ,3)
}
}
$this.data = $temp_array
To unconditionally turn a positive number into its negative equivalent (or, more generally, flip a number's sign), simply use the unary - operator:
PS> $v = 10; -$v
-10
Applied to your case:
$digit = -$this.data[$i,$j]
As an aside: If performance matters, you can speed up your loops by using .., the range operator to create the indices to iterate over:
$temp_array = New-Object 'object[,]' $this.row,$this.col
for ($i in 0..($this.row-1)) {
for ($j in 0..($this.col-1)) {
$digit = - $this.data[$i,$j]
$temp_array[$i,$j] = 1 / ( 1 + [math]::exp( $digit ) )
}
}
$this.data = $temp_array

Parsing binary data in 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>

Program to print permutation and combination for the given element

I have written the following code in PowerShell to display the permutation and combination for the given string but I am not able to achieve it by recursive functions.
function Swapping1 {
$a = "abc"
$b = $a.ToCharArray()
$c = $b.Length-1
$d = 0
GetPer($b, $d, $c)
}
function GetPer {
Param($list, $k, $m)
[char[]] $list1 = $list[0]
$k = $list[1]
$m = $list[2]
if ($k -eq $m) {
# Write-Host $list1
} else {
for ($i = $k; $i -le 1; $i++) {
$a = $list1[$k]
$b = $list1[$i]
$a, $b = $b, $a
$k = $k+1
Write-Host $list[0]
GetPer($list[0], $k, $m)
$a, $b = $b, $a
}
}
}
The same logic when I write it in C# it is working fine.
private static void Swap(ref char a, ref char b) {
if (a == b) return;
a ^= b;
b ^= a;
a ^= b;
}
private static void GetPermutation(char[] list, int k, int m) {
if (k == m) {
Console.Write(list);
Console.WriteLine();
} else
for (int i = k; i <= m; i++) {
Swap(ref list[k], ref list[i]);
GetPermutation(list, k + 1, m);
Swap(ref list[k], ref list[i]);
}
}
static void Main(string[] args) {
string str = "abc";
char[] arr = str.ToCharArray();
int x = arr.Length - 1;
GetPermutation(arr, 0, x);
//GetPer(arr);
Console.Read();
}
Below is the output when I executed the PowerShell script:
PS C:\WINDOWS\system32> C:\Dorababu\Power Shell\Swapping1.ps1
a b c
a b c
a b c
C# output:
Your C# and PowerShell code do different things. You output at a different place in PowerShell, the condition in the for loop is different ($i -le 1 vs i <= m), you increment $k in the PowerShell code ($k = $k+1) but not in the C# code, and your swapping as well as the parameter handling are completely wrong. A function call foo(a, b, c) passes an array a, b, c to the first parameter of foo() and nothing to the second and third parameter. The correct way to call foo() with three parameters a, b, and c in PowerShell would be foo a b c. And for swapping array elements simply use the array elements. Don't assign them to variables first.
If you actually implement the same logic the PowerShell code works as expected:
function Swapping1 {
$a = "abc"
$b = $a.ToCharArray()
$c = $b.Length-1
$d = 0
GetPer $b $d $c
}
function GetPer {
Param($list, $k, $m)
if ($k -eq $m) {
Write-Host $list
} else {
for ($i = $k; $i -le $m; $i++) {
$list[$k], $list[$i] = $list[$i], $list[$k]
GetPer $list ($k+1) $m
$list[$k], $list[$i] = $list[$i], $list[$k]
}
}
}
Swapping1

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)