nisus.com
https://www.nisus.com/forum/

arab numerals - roman numerals
https://www.nisus.com/forum/viewtopic.php?f=17&t=3789
Page 1 of 1

Author:  js [ 2010-06-12 03:04:40 ]
Post subject:  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?

Author:  Kino [ 2010-06-12 05:03:37 ]
Post subject:  Re: arab numerals - roman numerals

Old macros written before NWP 1.3; perhaps they can be simplified. Both work on selected numerals.
Code:
### 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:
### 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

Author:  js [ 2010-06-13 14:16:56 ]
Post subject:  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".

Author:  Kino [ 2010-06-13 18:35:17 ]
Post subject:  Re: arab numerals - roman numerals

js wrote:
The macro does not execute and complains about the length property "requiring an object under $matched".

That is no-break spaces inserted by php. Run
Code:
Replace All '\xA0', '\x20', 'E'
on your macro file.

Author:  js [ 2010-06-14 06:38:30 ]
Post subject:  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.

Author:  Kino [ 2010-06-14 08:03:54 ]
Post subject:  Re: arab numerals - roman numerals

js wrote:
for Arabic -> Roman
Duplicate the macro and just make lowercase the definitions of $romanIXCM and $romanVLD, i.e.
Code:
$romanIXCM = Array.new ('i', 'x', 'c', 'm')
$romanVLD = Array.new ('v', 'l', 'd')

Author:  Groucho [ 2010-06-15 08:05:52 ]
Post subject:  Re: arab numerals - roman numerals

You can specify which case with a prompt. Substitute this piece of code:
Code:
$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

for this
Code:
$romanIXCM = Array.new ('I', 'X', 'C', 'M')  # create an array
$romanVLD = Array.new ('V', 'L', 'D')  # create another array


Henry.

Author:  phspaelti [ 2010-06-17 17:24:31 ]
Post subject:  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.
Code:
### 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

Author:  martin [ 2010-06-18 10:37:11 ]
Post subject:  Re: arab numerals - roman numerals

phspaelti wrote:
So Roman numerals are much too stupid a technology for him

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.

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.

Author:  phspaelti [ 2010-06-19 08:49:49 ]
Post subject:  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:
Code:
### 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

Author:  phspaelti [ 2010-06-19 09:21:20 ]
Post subject:  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:
### 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

Page 1 of 1 All times are UTC - 8 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/