Popular Post czardas Posted October 11, 2016 Popular Post Share Posted October 11, 2016 (edited) I wrote this Roman numeral conversion function a while ago, but never got round to writing the reverse function. This is a very simple example aimed more at beginners: sometimes you need a break from trying to figure out the more complicated stuff. I decided to post this mini UDF just in case someone might make use of it. The regexp in the _IsRoman() validation function might also be of interest to some of the more advanced members. Maybe it can be improved. expandcollapse popup#include-once ; #INDEX# ====================================================================================================================== ; Title .........: Roman ; AutoIt Version : 3.3.14.2 ; Language ......: English ; Description ...: Roman numeral conversion and validation. ; Notes .........: Roman numerals range between 1 and 3999. ; Author(s) .....: czardas ; ============================================================================================================================== ; #CURRENT# ==================================================================================================================== ; _IsRoman ; _Roman ; _RomanToDec ; ============================================================================================================================== ; #FUNCTION# =================================================================================================================== ; Name...........: _IsRoman ; Description ...: Tests if a string is a roman numeral. ; Syntax.........: _IsRoman($sRoman) ; Parameters.....; $sRoman - The string to test. ; Return values .: Returns True or False and sets @error to 1 if the parameter is an empty string. ; Author ........: czardas ; ============================================================================================================================== Func _IsRoman($sRoman) If $sRoman = '' Then Return SetError(1, 0, False) $sRoman = StringRegExpReplace($sRoman, '(?i)(\A)(M{0,3})?(CM|DC{0,3}|CD|C{0,3})?(XC|LX{0,3}|XL|X{0,3})?(IX|VI{0,3}|IV|I{0,3})?', '') Return $sRoman = '' EndFunc ;==>_IsRoman ; #FUNCTION# =================================================================================================================== ; Name...........: _Roman ; Description ...: Converts a decimal integer to a roman numeral. ; Syntax.........: _Roman($iInt) ; Parameters.....; $iInt - The integer to convert. ; Return values .: Returns the integer converted to a Roman numeral. ; Sets @error to 1 and returns an empty string if the integer is out of bounds. ; Author ........: czardas ; ============================================================================================================================== Func _Roman($iInt) If Not StringIsInt($iInt) Or $iInt > 3999 Or $iInt < 1 Then Return SetError(1, 0, "") $iInt = Int($iInt) ; in case the string contains leading zeros Local $aNumeral[10][4] = _ [["","","",""], _ ["M","C","X","I"], _ ["MM","CC","XX","II"], _ ["MMM","CCC","XXX","III"], _ ["","CD","XL","IV"], _ ["","D","L","V"], _ ["","DC","LX","VI"], _ ["","DCC","LXX","VII"], _ ["","DCCC","LXXX","VIII"], _ ["","CM","XC","IX"]] Local $iOffset = StringLen($iInt) -4, $sRoman = "", $aDecimal = StringSplit($iInt, "", 2) For $i = 0 To UBound($aDecimal) -1 $sRoman &= $aNumeral[$aDecimal[$i]][$i -$iOffset] Next Return $sRoman EndFunc ;==>_Roman ; #FUNCTION# =================================================================================================================== ; Name...........: _RomanToDec ; Description ...: Converts a roman numeral to a decimal integer. ; Syntax.........: _RomanToDec($sRoman) ; Parameters.....; $sRoman - The Roman numeral to convert. ; Return values .: Returns the Roman numeral converted to an integer. ; Sets @error to 1 and returns an empty string if the Roman numeral is invalid. ; Author ........: czardas ; ============================================================================================================================== Func _RomanToDec($sRoman) If Not _IsRoman($sRoman) Then Return SetError(1, 0, '') Local $aChar = StringSplit($sRoman, '') For $i = 1 To $aChar[0] Switch $aChar[$i] Case 'I' $aChar[$i] = 1 Case 'V' $aChar[$i] = 5 Case 'X' $aChar[$i] = 10 Case 'L' $aChar[$i] = 50 Case 'C' $aChar[$i] = 100 Case 'D' $aChar[$i] = 500 Case 'M' $aChar[$i] = 1000 EndSwitch Next Local $iCount = 0, $iCurr, $iNext For $i = 1 To $aChar[0] $iCurr = $aChar[$i] If $i < $aChar[0] Then $iNext = $aChar[$i +1] If $iNext > $iCurr Then $iCurr = $iNext - $iCurr $i += 1 ; skip the next element EndIf EndIf $iCount += $iCurr Next Return $iCount EndFunc ;==>_RomanToDec Simple Demo #include <Array.au3> #include 'Roman.au3' Local $aRoman[4000] = [3999] For $i = 1 To 3999 $aRoman[$i] = _Roman($i) Next _ArrayShuffle($aRoman, 1) ; shuffle the array starting from Row 1 _ArrayDisplay($aRoman, "Shuffled") RomanSort($aRoman, 1) ; sort the roman numerals by size _ArrayDisplay($aRoman, "Sorted") Func RomanSort(ByRef $aArray, $iStart = Default, $iStop = Default) If Not IsArray($aArray) Or UBound($aArray, 0) <> 1 Then Return SetError(1) ; simple 1D array demo $iStart = ($iStart = Default ? 0 : Int($iStart)) $iStop = ($iStop = Default ? UBound($aArray) -1 : Int($iStop)) If $iStart < 0 Or $iStart > $iStop Or $iStop > UBound($aArray) -1 Then Return SetError(2) ; out of bounds For $i = $iStart To $iStop If Not _IsRoman($aArray[$i]) Then Return SetError(3) ; Roman numerals only [other solutions are beyond the scope of this simple demo] $aArray[$i] = _RomanToDec($aArray[$i]) ; convert to decimal Next _ArraySort($aArray, 0, $iStart, $iStop) ; sort integers For $i = $iStart To $iStop $aArray[$i] = _Roman($aArray[$i]) ; convert back to Roman numerals Next EndFunc ;==> RomanSort Edited October 11, 2016 by czardas page formatting l3ill, Gianni, TheSaint and 4 others 7 operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
UEZ Posted October 11, 2016 Share Posted October 11, 2016 (edited) I was always too lazy to write this conversion. Thanks for sharing. Edited October 11, 2016 by UEZ luny 1 Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
luny Posted October 11, 2016 Share Posted October 11, 2016 nice one Link to comment Share on other sites More sharing options...
czardas Posted October 11, 2016 Author Share Posted October 11, 2016 6 hours ago, UEZ said: I was always too lazy to write this conversion. I know the feeling. I'm waiting for someone to come up with a Natural Roman Sort algorithm (King Henry I, II, III, IV etc...). UEZ 1 operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
TheSaint Posted October 12, 2016 Share Posted October 12, 2016 (edited) Thanks for sharing. I well remember adding a Roman Numeral element to my old Titlecase Function ... was it turned (or a variation of) into a UDF ... not sure? There is a couple of discussions about the place somewhere ... involving @guinness, @tcurran, you and others ... if I remember rightly. If and when I ever get around to updating that function, then hopefully I will remember to incorporate your excellent UDF. Edited October 12, 2016 by TheSaint czardas 1 Make sure brain is in gear before opening mouth! Remember, what is not said, can be just as important as what is said. Spoiler What is the Secret Key? Life is like a Donut If I put effort into communication, I expect you to read properly & fully, or just not comment. Ignoring those who try to divert conversation with irrelevancies. If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it. I'm only big and bad, to those who have an over-active imagination. I may have the Artistic Liesense to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage) Link to comment Share on other sites More sharing options...
jchd Posted October 12, 2016 Share Posted October 12, 2016 If someone has some time for a roman challenge, here's one: A new hotel in Vegas has 2016 rooms and the tenant wants to number every room in roman numerals (in natural sequence). How many letters of each in ( I V X L ...) will that need? Algorithmic answer only, please. This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Gianni Posted October 12, 2016 Share Posted October 12, 2016 3 hours ago, jchd said: If someone has some time for a roman challenge.... a first attempt: #include <Array.au3> #include 'Roman.au3' Local $aRomanDigit = [['I', 0],['V', 0],['X', 0],['L', 0],['C', 0],['D', 0],['M', 0]] For $iRoom = 1 To 2016 _SumRomanDigit($iRoom, $aRomanDigit) Next _ArrayDisplay($aRomanDigit, 'Roman digits to buy') Func _SumRomanDigit($iNum, ByRef $aStack) Local $sRoman = _Roman($iNum) For $i = 0 To 6 ; check all possible roman digit IVXLCDM StringReplace($sRoman, $aStack[$i][0], '') ; count digit $aStack[$i][1] += @extended ; sum digit If $sRoman = "" Then ExitLoop ; speedup a bit Next EndFunc ;==>_SumRomanDigi Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
czardas Posted October 13, 2016 Author Share Posted October 13, 2016 (edited) I'm surprised at the level of interest shown here. Roman numerals are still a popular alternative numbering system though. There are a few other threads about this subject around here somewhere. I'm happy if someone finds it useful. Edited October 13, 2016 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
CarlD Posted July 26, 2017 Share Posted July 26, 2017 (edited) I recently wrote some quick and dirty decimal-to-Roman, Roman-to-decimal functions, then decided to check this forum and found this thread. For what it's worth, here's my code. It's compact, with minimal checking for bad input. Enjoy. expandcollapse popup; ROMANUM.AU3 ; Convert Arabic to Roman, Roman to Arabic numerals ; CLD -- XXVI IULIUS MMXVII If $CmdLine[0] Then $vArg = $CmdLine[1] $sFunc = "Roman" If StringinStr("MDCLXVI", StringLeft($vArg, 1)) Then $sFunc = "Arabic" $vOut = Call($sFunc, $vArg) If Not $vOut Then $vOut = "No output" Exit MsgBox(0, @ScriptName, $vOut) Else Exit MsgBox(64, @ScriptName, "Usage:" & @CRLF & @ScriptName & " decimal|roman_in") EndIf Func Arabic($sRom) ; Roman numerals to Arabic $aNum = StringSplit("CM,CD,XC,XL,IX,IV,M,D,C,L,X,V,I", ",") $aVal = StringSplit("900,400,90,40,9,4,1000,500,100,50,10,5,1", ",") $vOut = 0 For $i = 1 To $aVal[0] If StringInStr($sRom, $aNum[$i]) Then $sRom = StringReplace($sRom, $aNum[$i], "") $vOut += @extended * $aVal[$i] EndIf Next If $sRom Then Return SetError(1, 0, "Bad input") Else Return $vOut EndIf EndFunc ;==>Arabic ;---------- Func Roman($iIn) ; Arabic to Roman numerals $sOut = "" $aNum = StringSplit("M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I", ",") $aVal = StringSplit("1000,900,500,400,100,90,50,40,10,9,5,4,1", ",") For $i = 1 To $aVal[0] While $iIn >= Int($aVal[$i]) $sOut &= $aNum[$i] $iIn -= $aVal[$i] WEnd Next Return $sOut EndFunc ;==>Roman Edited July 26, 2017 by CarlD czardas 1 Link to comment Share on other sites More sharing options...
czardas Posted July 27, 2017 Author Share Posted July 27, 2017 (edited) Thanks for sharing! Edit - You ought to include some form of validity check because it erroneously returns values for invalid roman numeral sequences and out of range integers - it's something to think about. Edited July 27, 2017 by czardas CarlD 1 operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
CarlD Posted July 28, 2017 Share Posted July 28, 2017 (edited) On 7/27/2017 at 3:53 AM, czardas said: Thanks for sharing! Edit - You ought to include some form of validity check because it erroneously returns values for invalid roman numeral sequences and out of range integers - it's something to think about. Thanks, czardas. For my specific purpose I didn't need error checking, because I could rely on input being valid. For a general purpose utility, your UDF covers the territory nicely. I'm sure you know that you can expand the valid range by adding overlines to M, D, C, X, and V, which multiplies each value by 1,000. I've also read that double overlines can be used to multiply the values by 100,000 1,000,000. I haven't checked whether these overline characters are covered by Unicode. With double overlines, the valid range goes up to 399,999,999 3,999,999,999. Though I think even higher numbers would be very decipherable, e.g., {MMMM} for 400,000,000 4,000,000,000 (where the curly braces mean double overline), and on up. Correction. I was referring to this page: http://sorenwinslow.com/RomanNumerals.asp. The "100,000" in the text appears to be an error; the examples show that the double overline multiplier is 1,000,000, Edited July 28, 2017 by CarlD Link to comment Share on other sites More sharing options...
czardas Posted July 30, 2017 Author Share Posted July 30, 2017 (edited) On 28/07/2017 at 9:46 PM, CarlD said: I'm sure you know that you can expand the valid range by adding overlines to M, D, C, X, and V, which multiplies each value by 1,000. Hehe! No I wasn't aware of this convention, but it's good to know about. I'm okay with the limited range of 1 to 3999: I won't be doing any calculations with them. Having said that, perhaps it would be a fun challenge for someone to design a Roman calculator. Zero would throw an error (makes me giggle). Edited July 30, 2017 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
Skysnake Posted July 31, 2017 Share Posted July 31, 2017 Some historical chatter... Do you know why letters I, V and X are used for the lower numbers? It works like this: Start with your left hand, palm out. Raise your little finger (outside finger), this means "1" Add the ring finger, two. Add the middle finger, three. Now, drop little and ring fingers, raise middle finger, index and thumb, four. Drop middle finger, what remains is five. Keep five on the left hand, and add the right thumb, six. Etc Two thumbs crossed means ten. czardas 1 Skysnake Why is the snake in the sky? Link to comment Share on other sites More sharing options...
czardas Posted July 31, 2017 Author Share Posted July 31, 2017 (edited) Sounds like a good daily excersice for ukulele. Edited July 31, 2017 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
xroot Posted July 31, 2017 Share Posted July 31, 2017 Yes, overline characters are covered by Unicode. Here is a little test. expandcollapse popupglobal const $dbar = ChrW(0x33F) ;DOUBLE OVERLINE global const $bar = ChrW(0x305) ;OVERLINE Func I_Quit() Exit EndFunc Func To_Roman($iDec) local $roman = "" local $io[25][2] = [[1000000000,"M" & $dbar], _ [500000000,"D" & $dbar], _ [100000000,"C" & $dbar], _ [50000000,"L" & $dbar], _ [10000000,"X" & $dbar], _ [5000000,"V" & $dbar], _ [1000000,"M" & $bar], _ [500000,"D" & $bar], _ [100000,"C" & $bar], _ [50000,"L" & $bar], _ [10000,"X" & $bar], _ [5000,"V" & $bar], _ [1000,"M"], _ [900,"CM"], _ [500,"D"], _ [400,"CD"], _ [100,"C"], _ [90,"XC"], _ [50,"L"], _ [40,"XL"], _ [10,"X"], _ [9,"IX"], _ [5,"V"], _ [4,"IV"], _ [1,"I"]] For $i = 0 To Ubound($io) - 1 While $iDec >= $io[$i][0] $iDec -= $io[$i][0] $roman &= $io[$i][1] WEnd Next Return $roman EndFunc HotKeySet("{ESC}","I_Quit") local $iNumber = 3457898377 SplashTextOn("Roman Numbers For:" & $iNumber,To_Roman($iNumber),@DesktopWidth,60,-1,-1,-1,default,24) While True wEnd czardas and CarlD 2 Link to comment Share on other sites More sharing options...
CarlD Posted August 4, 2017 Share Posted August 4, 2017 On 7/31/2017 at 4:23 AM, Skysnake said: Some historical chatter... Do you know why letters I, V and X are used for the lower numbers? It works like this: Start with your left hand, palm out. Raise your little finger (outside finger), this means "1" Add the ring finger, two. Add the middle finger, three. Now, drop little and ring fingers, raise middle finger, index and thumb, four. Drop middle finger, what remains is five. Keep five on the left hand, and add the right thumb, six. Etc Two thumbs crossed means ten. Fascinating! And makes sense. But the lawyer in me asks: What is your authority for this? Link to comment Share on other sites More sharing options...
CarlD Posted August 4, 2017 Share Posted August 4, 2017 On 7/31/2017 at 10:24 AM, xroot said: Yes, overline characters are covered by Unicode. Here is a little test. expandcollapse popupglobal const $dbar = ChrW(0x33F) ;DOUBLE OVERLINE global const $bar = ChrW(0x305) ;OVERLINE Func I_Quit() Exit EndFunc Func To_Roman($iDec) local $roman = "" local $io[25][2] = [[1000000000,"M" & $dbar], _ [500000000,"D" & $dbar], _ [100000000,"C" & $dbar], _ [50000000,"L" & $dbar], _ [10000000,"X" & $dbar], _ [5000000,"V" & $dbar], _ [1000000,"M" & $bar], _ [500000,"D" & $bar], _ [100000,"C" & $bar], _ [50000,"L" & $bar], _ [10000,"X" & $bar], _ [5000,"V" & $bar], _ [1000,"M"], _ [900,"CM"], _ [500,"D"], _ [400,"CD"], _ [100,"C"], _ [90,"XC"], _ [50,"L"], _ [40,"XL"], _ [10,"X"], _ [9,"IX"], _ [5,"V"], _ [4,"IV"], _ [1,"I"]] For $i = 0 To Ubound($io) - 1 While $iDec >= $io[$i][0] $iDec -= $io[$i][0] $roman &= $io[$i][1] WEnd Next Return $roman EndFunc HotKeySet("{ESC}","I_Quit") local $iNumber = 3457898377 SplashTextOn("Roman Numbers For:" & $iNumber,To_Roman($iNumber),@DesktopWidth,60,-1,-1,-1,default,24) While True wEnd Lovely! Very nice. Now.... Link to comment Share on other sites More sharing options...
Skysnake Posted August 8, 2017 Share Posted August 8, 2017 res ipsa loquitur Skysnake Why is the snake in the sky? Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now