//  Roman Numerals
//  Written:  2009-01-22 by James Alarie <jalarie@umich.edu>
//    http://spruce.flint.umich.edu/~jalarie/
//
//  Usage:
//    RomanNumeralVersion=ToRoman(DecimalVersion);
//    DecimalVersion=ToRoman(RomanNumeralVersion);
//
//  Codes for values up to 1000 are upper-case, as usual, but I can't do 
//  bars over the letters to represent multiples of 1000, so these large 
//  values are returned as lower-case letters:
//
//    I = 1       i = 1000
//    V = 5       v = 5000
//    X = 10      x = 10000
//    L = 50      l = 50000
//    C = 100     c = 100000
//    D = 500     d = 500000
//    M = 1000    m = 1000000
//
//  The "i" code is always returned as "M" by the "ToRoman" function because 
//  the "M" and "i" values are equal.  The "i" will be properly processed by 
//  the "FromRoman" function.
//
//  A negative value passed to the ToRoman function will produce a result 
//  with a leading minus (-) sign:
//
//    -14 --> -XIV
//
//  If the value passed to the ToRoman function contains a decimal fraction, 
//  the result will be a Roman Numeral value for the integer part, a decimal 
//  (.) point, a single Roman Numeral character for the denominator part of 
//  the fraction, and the Roman Numeral representation of the fraction part: 
//
//    714.63 --> DCCXIV.CLXIII
//    1.4    --> I.XIV              4 tenths
//    1.04   --> I.CIV              4 hundreths
//    1.004  --> I.MIV              4 thousandths
//
//  The FromRoman function can handle negatives and fractions as defined for 
//  the ToRoman function.

        function ToRoman(What) {
          var Given=What;
          var Return='';
          if (isNaN(Given)) {
            Return='*** Error';
          } else {                                  // no problems
            var NegativeSwitch='no', Fraction=0, ix1;
            if (Given < 0) {
              NegativeSwitch='yes';
              Given=0-Given;
            }
            Given=String(Given);
            if ((ix1=Given.indexOf('.')) > -1) {    // a fraction exists
              Fraction=Given.substring(ix1+1);
              Given=Given.substring(0,ix1);
            }
            Return=ToRoman2(Given);
            if ((ix1=Fraction.length) > 0) {
              var Return2;
              if (ix1 == 1) { Return+='.X'; }
              if (ix1 == 2) { Return+='.C'; }
              if (ix1 == 3) { Return+='.M'; }
              if (ix1 == 4) { Return+='.x'; }
              if (ix1 == 5) { Return+='.c'; }
              if (ix1 >= 6) { Return+='.m'; Fraction=Fraction.substring(0,6); }
              Return2=ToRoman2(Fraction);
              Return+=Return2;
            }
            if (NegativeSwitch == 'yes') {
              Return='-'+Return;
            }
          }
          return Return;
        } // ToRoman
        function ToRoman2(Given) {
          var Return2='';
          var Codes=new Array(
            'm',1000000, 'Im',999999, 'Vm',999995, 'Xm',999990, 
            'Lm',999950, 'Cm',999900, 'Dm',999500, 'Mm',999000, 
            'im',999000, 'vm',995000, 'xm',990000, 'lm',950000, 
            'cm',900000, 'd',500000, 'Id',499999, 'Vd',499995, 'Xd',499990, 
            'Ld',499950, 'Cd',499900, 'Dd',499500, 'Md',499000, 
            'vd',495000, 'xd',490000, 'ld',450000, 'cd',400000, 'c',100000, 
            'Ic',99999, 'Vc',99995, 'Xc',99990, 'Lc',99950, 'Cc',99900, 
            'Dc',99500, 'Mc',99000, 'vc',95000, 'xc',90000, 'l',50000, 
            'Il',49999, 'Vl',49995, 'Xl',49990, 'Ll',49950, 'Cl',49900, 
            'Dl',49500, 'Ml',49000, 'vl',45000, 'xl',40000, 'x',10000, 
            'Ix',9999, 'Vx',9995, 'Xx',9990, 'Lx',9950, 'Cx',9900, 
            'Dx',9500, 'Mx',9000, 'v',5000, 'Iv',4999, 'Vv',4995, 
            'Xv',4990, 'Lv',4950, 'Cv',4900, 'Dv',4500, 'Mv',4000, 
            'M',1000, 'IM',999, 'VM',995, 'XM',990, 'LM',950, 'CM',900, 
            'D',500, 'ID',499, 'VD',495, 'XD',490, 'LD',450, 'CD',400, 
            'C',100, 'IC',99, 'VC',95, 'XC',90, 'L',50, 'IL',49, 'VL',45, 
            'XL',40, 'X',10, 'IX',9, 'V',5, 'IV',4, 'I',1);
          var ix1;
          var Value;
          for (ix1=0; ix1<Codes.length-1; ix1=ix1+2) {
            Value=Codes[ix1+1];
            while (Given >= Value) { Return2+=Codes[ix1]; Given-=Value; }
          }
          return Return2;
        } // ToRoman2
        
        function FromRoman(What) {
          var Given=What;
          var Return=0, NegativeSwitch='no';
          if (Given.substring(0,1) == '-') {
            NegativeSwitch='yes';
            Given=Given.substring(1);
          }
          if (Given.match(/^([IVXLCDMivxlcdm])*\.{0,1}([IVXLCDMivxlcdm]*)$/)) {
            var Integer, Fraction, ix1;
            if ((ix1=Given.indexOf('.')) > -1) {    // a fraction exists
              Integer=Given.substring(0,ix1);
              Fraction=Given.substring(ix1+1);
              Return=FromRoman2(Integer);
              var Denominator=FromRoman(Fraction.substring(0,1));
              var Numerator=FromRoman(Fraction.substring(1));
              Return=Return+Numerator/Denominator;
            } else {                                // no fractional part
              Return=FromRoman2(Given);
            }
            if (NegativeSwitch == 'yes') {
              Return=0-Return;
            }
          } else {                                  // invalid codes exist
            Return='*** Error';
          }
          return Return;
        } // FromRoman
        function FromRoman2(Given) {
          var Return=0;
          var Singles=new Array();
          Singles['m']=1000000;  Singles['d']=500000;  Singles['c']=100000;
          Singles['l']=50000;    Singles['x']=10000;   Singles['v']=5000;
          Singles['i']=1000;
          Singles['M']=1000;     Singles['D']=500;     Singles['C']=100;
          Singles['L']=50;       Singles['X']=10;      Singles['V']=5;
          Singles['I']=1;
          var ix1, Value, OldValue=0;
          while ((ix1=Given.length) > 0) {
            Work=Given.substr(ix1-1);
            Given=Given.substr(0,ix1-1);
            Value=Singles[Work];
            if (Value >= OldValue) { Return=Return*1+Value; } 
            else                   { Return=Return*1-Value; }
            OldValue=Value;
          }
          return Return;
        } // FromRoman2
