arab numerals - roman numerals
arab numerals - roman numerals
Has there ever been a macro to output roman numerals from arab ones and vice versa? Or maybe this is not as simple as that?
Re: arab numerals - roman numerals
Old macros written before NWP 1.3; perhaps they can be simplified. Both work on selected numerals.
Code: Select all
### Arabic Numeral to Roman Numeral ###
# Convert Arabic numeral(s) in selection(s) to Roman numeral(s).
# Only numerals from 1 to 3999 are supported.
Require Application Version '3.2'
$doc = Document.active
$thousands = ','
$comma = System Property 'use comma for decimal point'
if $comma == true
$thousands = '.'
end
$findArabicNumerals = '[0-9]+(?:[0-9' & $thousands # construct a find expression for Arabic numerals
$findArabicNumerals &= '])*'
$numfound = Find All $findArabicNumerals, 'Es' # E: PowerFind Pro; s: in selections
if ! $numfound
Exit 'No Arabic numeral found in selection(s), exit...'
end
$selections = $doc.textSelections
$romanIXCM = Array.new ('I', 'X', 'C', 'M') # create an array
$romanVLD = Array.new ('V', 'L', 'D') # create another array
$errorMessage = ''
foreach $sel in reversed $selections
$validArabicNumeral = true
$roman = ''
$arabic = $sel.subtext
$arabic.replaceAll $thousands, ''
if $arabic > 3999
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is greater than 3999.\n"
end
if $arabic < 1
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is less than 1.\n"
end
if $validArabicNumeral == true
$arabic = $arabic.split ''
$i = 0
$j = $arabic.count - 1
while $i < $j # reverse values in $arabic
$arabic.swapValuesAtIndexes $i, $j
$i += 1
$j -= 1
end
$limit = $arabic.count
$i = 0
while $i < $limit
if $arabic[$i] == 4
# When $i is 0, $romanIXCM[0] is "I" and $romanVLD[0] is "V".
$roman = $romanVLD[$i] & $roman
$roman = $romanIXCM[$i] & $roman
elsif $arabic[$i] == 9
$roman = $romanIXCM[$i+1] & $roman
$roman = $romanIXCM[$i] & $roman
elsif $arabic[$i] < 4
# When $i is 2, $romanIXCM[2] is "C" and $romanIXCM[2+1] is "M".
# When $i is 3, $romanIXCM[3] is "M" and $arabic[3] is "1".
$c = $arabic[$i]
while $c > 0
$roman = $romanIXCM[$i] & $roman
$c -= 1
end
else # if $arabic[$i] is 6 or 7 or 8
# When $i is 1, $romanVLD[1] is "L", $romanIXCM[1] is "X"
# and $arabic[1] is "8". So ($arabic[1] - 5) = 3.
$c = $arabic[$i] - 5
while $c > 0
$roman = $romanIXCM[$i] & $roman
$c -= 1
end
$roman = $romanVLD[$i] & $roman
end
$i += 1
end
$roman = Cast to String $roman
$sel.text.replaceInRange $sel.range, $roman
end
end
if $errorMessage
Exit $errorMessage
end
Code: Select all
### Roman Numeral to Arabic Numeral ###
# Convert Roman numeral(s) in selection(s) to Arabic numeral(s).
# Only numbers from 1 to 3999 are supported.
Require Application Version '3.1'
$doc = Document.active
$selections = $doc.textSelections
$errorMessage = ''
if ! $selections.firstValue.length
Exit 'Nothing selected, exit...'
end
$errorMessage = ''
$roman2arabic = Hash.new
$roman2arabic{'I'} = '1'
$roman2arabic{'V'} = '5'
$roman2arabic{'X'} = '10'
$roman2arabic{'L'} = '50'
$roman2arabic{'C'} = '100'
$roman2arabic{'D'} = '500'
$roman2arabic{'M'} = '1000'
foreach $sel in reversed $selections
$roman = $sel.subtext
$roman.replaceAll '\x20|\xA0', '', 'E'
# Transformm, for example, "M CM XL III" to "MCMXLIII"
# \x20 space
# \xA0 no-break space
# | OR operator
$matched = $roman.find '(?x) # See if it is a valid roman numeral
^(
(?: M{1,3} )?
(?: D?C{1,3} | C?[DM] )?
(?: L?X{1,3} | X?[LC] )?
(?: V?I{1,3} | I?[VX] )?
)$', 'Ei' # (?x) eXtended mode (ignore white space)
# ^ strings start
# ( ) group (captured)
# (?: ) group (not captured)
# ? zero or one time
# {1,3} match at least 1 but not more than 3 times
# | OR operator
if ! $matched.length # if $matched is empty, then it is an invalid roman numeral
$errorMessage &= "\"$roman\" is not a valid Roman numeral.\n"
else
$roman.replaceAll '(?=I[VX])|(?=X[LC])|(?=C[DM])', '-', 'Ei'
# insert "-" before
# "I" preceding "V" or "X",
# "X" preceding "L" or "C" and
# "C" preceding "D" or "M"
# because they are negative
# to transform "MCMXLIII" into "M-CM-XLIII"
$roman.replaceAll '(?<!-)(?!-)(?=\S)', '+', 'E'
# insert "+" before non-minus character
# and non-minus character
# to transform "M-CM-XLIII" into "+M-C+M-X+L+I+I+I"
$range = Range.new 0, $roman.length
$roman.transliterateInRange $range, $roman2arabic # replace "I" with "1"
$i = 0
$plus = $minus = Array.new
$plusSels = $roman.findAll '(?<=\+)[^+-]+', 'E'
foreach $n in $plusSels
$plus.appendValue $n.subtext
end
foreach $n in $plus
$i += $n
end
$minusSels = $roman.findAll '(?<=-)[^+-]+', 'E'
foreach $n in $minusSels
$minus.appendValue $n.subtext
end
foreach $n in $minus
$i -= $n
end
$sel.text.replaceInRange $sel.range, $i
end
end
if $errorMessage # if $errorMessage is not empty
Exit $errorMessage # show $errorMessage
end
Re: arab numerals - roman numerals
I can see that this is not so simple indeed.
I find a problem on the roman Arabic side though. The macro does not execute and complains about the length property "requiring an object under $matched".
I find a problem on the roman Arabic side though. The macro does not execute and complains about the length property "requiring an object under $matched".
Re: arab numerals - roman numerals
That is no-break spaces inserted by php. Runjs wrote:The macro does not execute and complains about the length property "requiring an object under $matched".
Code: Select all
Replace All '\xA0', '\x20', 'E'
Re: arab numerals - roman numerals
Thank you. that works fine now.
I have a suggestion to make: Probably it would be a good idea to provide for lowercase roman, as is often used in Prefaces. For Roman -> Arabic one can of course just start the Macro with the line "Convert:To UPPERCASE", while for Arabic -> Roman the (multiple) selections should be kept to change from upper to lower case if needed.
I have a suggestion to make: Probably it would be a good idea to provide for lowercase roman, as is often used in Prefaces. For Roman -> Arabic one can of course just start the Macro with the line "Convert:To UPPERCASE", while for Arabic -> Roman the (multiple) selections should be kept to change from upper to lower case if needed.
Re: arab numerals - roman numerals
Duplicate the macro and just make lowercase the definitions of $romanIXCM and $romanVLD, i.e.js wrote:for Arabic -> Roman
Code: Select all
$romanIXCM = Array.new ('i', 'x', 'c', 'm')
$romanVLD = Array.new ('v', 'l', 'd')
Re: arab numerals - roman numerals
You can specify which case with a prompt. Substitute this piece of code:
for this
Henry.
Code: Select all
$Case = prompt options "Do you want roman numerals to be uppercase or lowercase?", "", "OK", "UPPERCASE", "lowercase"
if $Case == "UPPERCASE"
$romanIXCM = Array.new ('I', 'X', 'C', 'M') # create an array
$romanVLD = Array.new ('V', 'L', 'D') # create another array
elsif $Case == "lowercase"
$romanIXCM = Array.new ('i', 'x', 'c', 'm') # create an array
$romanVLD = Array.new ('v', 'l', 'd') # create another array
end
Code: Select all
$romanIXCM = Array.new ('I', 'X', 'C', 'M') # create an array
$romanVLD = Array.new ('V', 'L', 'D') # create another array
Re: arab numerals - roman numerals
I know Kino is a very clever person, and excellent macro writer. So Roman numerals are much too stupid a technology for him. I think his approach to this problem is way too obscure. Sometimes less is more.
So here is my approach. I have stolen Kino's excellent approach to handling the input, but I replaced the actual conversion with what I think is a much more intuitive approach. This code is also certainly easier to maintain.
So here is my approach. I have stolen Kino's excellent approach to handling the input, but I replaced the actual conversion with what I think is a much more intuitive approach. This code is also certainly easier to maintain.
Code: Select all
### Arabic Numeral to Roman Numeral ###
# Convert Arabic numeral(s) in selection(s) to Roman numeral(s).
# Only numerals from 1 to 3999 are supported.
Require Application Version '3.2'
$doc = Document.active
$thousands = ','
$comma = System Property 'use comma for decimal point'
if $comma == true
$thousands = '.'
end
$findArabicNumerals = '[0-9]+(?:[0-9' & $thousands # construct a find expression for Arabic numerals
$findArabicNumerals &= '])*'
$numfound = Find All $findArabicNumerals, 'Es' # E: PowerFind Pro; s: in selections
if ! $numfound
Exit 'No Arabic numeral found in selection(s), exit...'
end
$selections = $doc.textSelections
$errorMessage = ''
foreach $sel in reversed $selections
$validArabicNumeral = true
$roman = ''
$arabic = $sel.subtext
$arabic.replaceAll $thousands, ''
if $arabic > 3999
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is greater than 3999.\n"
end
if $arabic < 1
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is less than 1.\n"
end
if $validArabicNumeral
while $arabic > 999
$arabic -= 1000
$roman &= ‘M’
end
if $arabic > 499
$arabic -= 500
$roman &= ‘D’
end
while $arabic > 99
$arabic -= 100
$roman &= ‘C’
end
if $arabic > 49
$arabic -= 50
$roman &= ‘L’
end
while $arabic > 9
$arabic -= 10
$roman &= ‘X’
end
if $arabic > 4
$arabic -= 5
$roman &= ‘V’
end
while $arabic > 0
$arabic -= 1
$roman &= ‘I’
end
$roman.findAndReplace ‘DCCCC’, ‘CM’
$roman.findAndReplace ‘CCCC’, ‘CD’
$roman.findAndReplace ‘LXXXX’, ‘XC’
$roman.findAndReplace ‘XXXX’, ‘XL’
$roman.findAndReplace ‘VIIII’, ‘IX’
$roman.findAndReplace ‘IIII’, ‘IV’
$sel.text.replaceInRange $sel.range, $roman
end
end
if $errorMessage
Exit $errorMessage
end
philip
- martin
- Official Nisus Person
- Posts: 5228
- Joined: 2002-07-11 17:14:10
- Location: San Diego, CA
- Contact:
Re: arab numerals - roman numerals
haha, yes, Roman numerals are a pain to work with. I don't know anything about the history behind them, but they seem quite impractical as compared to Arabic numbers.phspaelti wrote:So Roman numerals are much too stupid a technology for him
It'd be nice if the macro language gave a coder access to all the built-in number formatters, though I don't think such a feature would be used often.
Re: arab numerals - roman numerals
Well I think it is probably wrong to think that Roman numerals are numbers in any sense that we know. They really just an abbreviatory way to write out numbers as words.
One thing though is that Roman numerals reflect pretty directly the numbers as represented on an abacus. Presumably Romans would never have done math on paper the way we do, but would have done all math on an abacus.
Anyhow, my last attempt at this macro was a bit too primitive. Here is a slightly improved version that makes use of a hash:
One thing though is that Roman numerals reflect pretty directly the numbers as represented on an abacus. Presumably Romans would never have done math on paper the way we do, but would have done all math on an abacus.
Anyhow, my last attempt at this macro was a bit too primitive. Here is a slightly improved version that makes use of a hash:
Code: Select all
### Arabic Numeral to Roman Numeral ###
# Convert Arabic numeral(s) in selection(s) to Roman numeral(s).
# Only numerals from 1 to 3999 are supported.
Require Application Version '3.2'
$doc = Document.active
$thousands = ','
$comma = System Property 'use comma for decimal point'
if $comma == true
$thousands = '.'
end
$findArabicNumerals = '[0-9]+(?:[0-9' & $thousands # construct a find expression for Arabic numerals
$findArabicNumerals &= '])*'
$numfound = Find All $findArabicNumerals, 'Es' # E: PowerFind Pro; s: in selections
if ! $numfound
Exit 'No Arabic numeral found in selection(s), exit...'
end
$selections = $doc.textSelections
$errorMessage = ''
$romanNumerals = Hash.new 1,‘I’, 5,‘V’, 10,‘X’, 50,‘L’, 100,‘C’, 500,‘D’, 1000,‘M’
$romanDigitValues = $romanNumerals.keys
$romanDigitValues.sort
foreach $sel in reversed $selections
$validArabicNumeral = true
$roman = ''
$arabic = $sel.subtext
$arabic.replaceAll $thousands, ''
if $arabic > 3999
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is greater than 3999.\n"
end
if $arabic < 1
$validArabicNumeral = false
$errorMessage &= 'Cannot convert ' & $sel.subtext
$errorMessage &= " which is less than 1.\n"
end
if $validArabicNumeral
foreach $romanDigitValue in reversed $romanDigitValues
while $arabic >= $romanDigitValue
$arabic -= $romanDigitValue
$roman &= $romanNumerals{$romanDigitValue}
end
end
$roman.findAndReplace ‘DCCCC’, ‘CM’
$roman.findAndReplace ‘CCCC’, ‘CD’
$roman.findAndReplace ‘LXXXX’, ‘XC’
$roman.findAndReplace ‘XXXX’, ‘XL’
$roman.findAndReplace ‘VIIII’, ‘IX’
$roman.findAndReplace ‘IIII’, ‘IV’
$sel.text.replaceInRange $sel.range, $roman
end
end
if $errorMessage
Exit $errorMessage
end
philip
Re: arab numerals - roman numerals
And here also a version for the reverse conversion. Again I used the input/output of Kino's macro, and just 'simplified' the conversion. I also 'loosened' the types of numerals the macro accepts.
Code: Select all
### Roman Numeral to Arabic Numeral ###
# Convert Roman numeral(s) in selection(s) to Arabic numeral(s).
# Only numbers from 1 to 3999 are supported.
Require Application Version '3.1'
$doc = Document.active
$selections = $doc.textSelections
$errorMessage = ''
if ! $selections.firstValue.length
Exit 'Nothing selected, exit...'
end
$errorMessage = ''
$romanNumerals = Hash.new ‘I’,1, ‘V’,5, ‘X’,10, ‘L’,50, ‘C’,100, ‘D’,500, ‘M’,1000
foreach $sel in reversed $selections
$roman = $sel.subtext
$roman.replaceAll '\x20|\xA0', '', 'E'
# Transform, for example, "M CM XL III" to "MCMXLIII"
# \x20 space
# \xA0 no-break space
# | OR operator
$isRoman = $roman.find '(?x)^(
(?: M*)?
(?: D?C{1,4} | C?[DM])?
(?: L?X{1,4} | X?[LC])?
(?: V?I{1,4} | I?[VX])?
)$', 'Ei' # ^ strings start
# ( ) group (captured)
# (?: ) group (not captured)
# ? zero or one time
# {1,3} match at least 1 but not more than 3 times
# | OR operator
if ! $isRoman
$errorMessage &= "\"$roman\" is not a valid Roman numeral.\n"
else
$romanLetters = $roman.split ‘’
$arabic = 0
$lastLetterValue = 0
foreach $letter in $romanLetters
$letterValue = $romanNumerals{$letter}
if $lastLetterValue < $letterValue
$arabic -= $lastLetterValue
else
$arabic += $lastLetterValue
end
$lastLetterValue = $letterValue
end
$arabic += $lastLetterValue
$sel.text.replaceInRange $sel.range, $arabic
end
end
if $errorMessage # if $errorMessage is not empty
Exit $errorMessage # show $errorMessage
end
philip