Roman numerals/Encode - Rosetta Code Roman numerals/Encode Jump to: navigation , search Roman numerals/Encode You are encouraged to solve this task according to the task description, using any language you may know. Task Create a function taking a positive integer as its parameter and returning a string containing the Roman numeral representation of that integer. Modern Roman numerals are written by expressing each digit separately, starting with the left most digit and skipping any digit with a value of zero.

In Roman numerals: 1990 is rendered: 1000=M, 900=CM, 90=XC; resulting in MCMXC 2008 is written as 2000=MM, 8=VIII; or MMVIII 1666 uses each Roman symbol in descending order: MDCLXVI Contents var lookup:Object = {M:1000, CM:900, D:500, CD:400, C:100, XC:90, L:50, XL:40, X:10, IX:9, V:5, IV:4, I:1}; var roman:String = "", i:String; trace("1990 in roman is " + arabic2roman(1990)); trace("2008 in roman is " + arabic2roman(2008)); trace("1666 in roman is " + arabic2roman(1666)); 1990 in roman is MCMXC 2008 in roman is MMVIII 1666 in roman is MD

CLXVI And the reverse: var lookup:Object = {I:1, V:5, X:10, L:50, C:100, D:500, M:1000}; var num:Number = 0, val:Number = 0; while (romanArr.length) { num += val * (val < lookup[romanArr[0]] ?

-1 : 1); } trace("MCMXC in arabic is " + roman2arabic("MCMXC")); trace("MMVIII in arabic is " + roman2arabic("MMVIII")); trace("MDCLXVI in arabic is " + roman2arabic("MDCLXVI")); Output: function To_Roman (Number : Positive) return String is subtype Digit is Integer range 0..9; function Roman (Figure : Digit; I, V, X : Character) return String is begin when 2 => return I & I; when 3 => return I & I & I; when 4 => return I & V; when 5 => return "" & V; when 6 => return V & I; when 7 => return V & I & I; when 8 => return V & I & I & I; when 9 => return I & X; end case; pragma Assert (Number >= 1 and Number < 4000); return Roman (Number / 100 mod 10, 'C', 'D', 'M') & Roman (Number / 10 mod 10, 'X', 'L', 'C')

 & Roman (Number mod 10, 'I', 'V', 'X'); end To_Roman; Works with: ALGOL 68 version Revision 1 - no extensions to language used Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8-8d []CHAR roman = "MDCLXVmdclxvi"; # UPPERCASE for thousands # []CHAR adjust roman = "CCXXmmccxxii"; []INT arabic = (1000000, 500000, 100000, 50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1); []INT adjust arabic = (100000, 100000, 10000, 10000, 100

0, 1000, 100, 100, 10, 10, 1, 1, 0); PROC arabic to roman = (INT dclxvi)STRING: ( INT in := dclxvi; # 666 # FOR scale TO UPB roman WHILE in /= 0 DO INT multiples = in OVER arabic[scale]; in -:= arabic[scale] * multiples; IF in >= -adjust arabic[scale] + arabic[scale] THEN in -:= -adjust arabic[scale] + arabic[scale]; out +:= adjust roman[scale] + roman[scale] FI FOR key TO UPB test DO INT val = test[key]; print((val, " - ", arabic to roman(val), new line)) OD ) Output: (last example is manually wrapped): +1 - i +2 - ii +3 - iii +4 - iv +5 - v +6 - vi +7 - vii +8 - viii +9 - ix +10 - x +11 - xi +12 - xii +13 - xiii +14 - xiv +15 - xv

 +16 - xvi +17 - xvii +18 - xviii +19 - xix +20 - xx +25 - xxv +30 - xxx +40 - xl +50 - l +60 - lx +69 - lxix +70 - lxx +80 - lxxx +90 - xc +99 - xcix +100 - c +200 - cc +300 - ccc +400 - cd +500 - d +600 - dc +666 - dclxvi +700 - dcc +800 - dccc +900 - cm +1000 - m +1009 - mix +1444 - mcdxliv +1666 - mdclxvi +1945 - mcmxlv +1997 - mcmxcvii +1999 - mcmxcix +2000 - mm +2008 - mmviii +2500 - mmd +3000 - mmm +4000 - mV +4

999 - mVcmxcix +5000 - V +6666 - Vmdclxvi +10000 - X +50000 - L +100000 - C +500000 - D +1000000 - M +2147483647 - MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMM

MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMM

MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMCDLXXXmmmdcxlvii Works with: awtoc version any - tested with release Mon Apr 27 14:25:27 NZST 2009 BEGIN PROCEDURE ROMAN (INTEGER VALUE NUMBER; STRING(15) RESULT CHARACTERS; INTEGER RESULT LENGTH); COMMENT Returns the Roman number of an integer between 1 and 3999.

"MMMDCCCLXXXVIII" (15 characters long) is the longest Roman number under 4000; BEGIN PROCEDURE APPEND (STRING(1) VALUE C); BEGIN CHARACTERS(LENGTH|1) := C; LENGTH := LENGTH + 1 END; PROCEDURE I; APPEND(CASE PLACE OF ("I","X","C","M")); PROCEDURE V; APPEND(CASE PLACE OF ("V","L","D")); PROCEDURE X; APPEND(CASE PLACE OF ("X","C","M")); ASSERT (NUMBER >= 1) AND (NUMBER < 4000); CASE NUMBER DIV POWER + 1 OF BEGIN BEGIN END; BEGIN I; I; I END; BEGIN I; V END; BEGIN V; I; I END; BEGIN V; I; I; I END; BEGIN I; X END ROMAN(1, S, I); WRITE(S, I); ROMAN(3999, S, I); WRITE(S, I); ROMAN(3888, S, I); WRITE(S, I); ROMAN(2009, S, I); WRITE(S, I); ROMAN(405, S, I); WRITE

(S, I); END.

set n to remainder of a set v to item 1 of lstPair {remainder:n mod v, roman:(roman of a) & ¬ replicate(n div v, item 2 of lstPair)} end if roman of foldl(romanDigits, {remainder:n, roman:""}, ¬ [[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"]]) end roman map(roman, [2016, 1990, 2008, 2000, 1666]) --> {"MMXVI", "MCMXC", "MMVIII", "MM", "MDCLXVI"} end run -- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs) set lng to length of xs repeat with i

 from 1 to lng set v to lambda(v, item i of xs, i, xs) end repeat -- map :: (a -> b) -> [a] -> [b] on map(f, xs) set lng to length of xs set lst to {} repeat with i from 1 to lng set end of lst to lambda(item i of xs, i, xs) end repeat -- Egyptian multiplication - progressively doubling a list, appending -- stages of doubling to an accumulator where needed for binary -- assembly of a target length if class of a is list then set out to {} if n < 1 then return out set dbl to a if (n mod 2) > 0 then set out to out & dbl set n to (n div 2) set dbl to (dbl & dbl) end repeat -- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: Handler -> Script if class of f is script then f for (i=1; i<=leng; i++) { n = arr[i] number = int(number) # force to integer if (number < 1 || number > 3999) { # number is too small | big return split("I II III IV V VI VII

 VIII IX",roman1," ") # 1 2 ...

9 split("X XX XXX XL L LX LXX LXXX XC",roman10," ") # 10 20 ... 90 split("C CC CCC CD D DC DCC DCCC CM",roman100," ") # 100 200 ...

900 split("M MM MMM",roman1000," ") # 1000 2000 3000 v = (number - (number % 1000)) / 1000 number = number % 1000 w = (number - (number % 100)) / 100 number = number % 100 x = (number - (number % 10)) / 10 y = number % 10 return(roman1000[v] roman100[w] roman10[x] roman1[y]) } Works with: FreeBASIC DIM SHARED arabic(0 TO 12) AS Integer => {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 } DIM SHARED roman(0 TO 12) AS String*2 => {"M", "CM", "D","CD", "C","XC","L","XL","X","IX","V","IV","I"}

 FUNCTION toRoman(value AS Integer) AS String DIM i AS Integer FOR i = 0 TO 12 DO WHILE value >= arabic(i) 70 INPUT "Enter an arabic number: ";V 80 LET VALUE=V 100 FOR I=0 TO 12 110 READ A,R$ 120 IF V<A THEN GO TO 160 130 LET V$=V$+R$ 140 LET V=V-A set cnt=0& for %%R in (M,CM,D, CD ,C,XC,L,XL,X,IX,V,IV,I) do ( set rom!cnt!=%%R& set /a cnt+=1) ::Testing if !value!

lss !arab%a%! goto :EOF set result=!result!!rom%a%!

arabic = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 } roman$ = {"M", "CM", "D","CD", "C","XC","L","XL","X","IX","V","IV","I"} for i = 0 to 12 while value >= arabic[i] LOCAL i%, r$, arabic%(), roman$() DIM arabic%(12), roman$(12) arabic%() = 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900,1000 roman$() = "I","IV", "V","IX", "X","XL", "L","XC", "C","CD", "D","CM", "M" FOR i% = 12 TO 0 STEP -1 WHILE n% >= arabic%(i%) Reads the number to convert from standard input.

No range validation is performed. &>0\0>00p:#v_$ >:#,_ $ @ 4-v >5+#:/#<\55+%:5/\5%: vv_$9+00g+5g\00g8+>5g\00 g>\20p>:10p00g \#v _20gv > 2+ v^-1g01\g5+8<^ +9 _ IVXLCDM = indian roman cifr tenfoldroman letter tenfold . !arg:#?indian & 1990 2008 1666 3888 3999 4000:?NS & whl | str$("Can't convert " !N " to Roman numeral") ) ); Output: * Writes the Roman numeral representing n into the buffer s. * Handles up to n = 3999. * Since C doesn't have exceptions, n = 0 causes the whole program to exit * unsuccessfully. * s should be have room for at least 16 characters, including the trailing * null.

void roman(char *s, unsigned int n) { #define digits(loop, num, c1, c2) \ loop (n >= num) \ digits ( if, 900, 'C', 'M' ) digit ( if, 500, 'D' ) digits ( if, 400, 'C', 'D' ) digit ( while, 100, 'C' ) digits ( if, 90, 'X', 'C' ) digit ( if, 50, 'L' ) digits ( if, 40, 'X', 'L' ) digit ( while, 10, 'X' ) digits ( if, 9, 'I', 'X' ) digit ( if, 5, 'V' ) digits ( if, 4, 'I', 'V' ) digit ( while, 1, 'I' ) for (i = 1 ; i < 4000 ; ++i) { printf ("%4u: %s\n", i, buffer); } An alternative version which builds the string backwards.

char *ToRoman(int num, char *buf, int buflen) { static const char romanDgts[] = "ivxlcdmVXLCDM_"; char *roman = buf + buflen; *--roman = '\0'; /* null terminate return string */ if (num >= 4000000) { printf ("Number Too Big.\n"); return NULL; for (rdix = 0; rdix < strlen (romanDgts); rdix += 2) { if (num == 0) break; *--roman = romanDgts[rdix+1+v]; *--roman = romanDgts[rdix]; if (roman < buf+r+v) { printf ("Buffer too small."); Most straightforward (nothing elegant about it, but it's simple, and can calculate output length) #include <stdio.h> int to_roman(char *out, int n) { if (n <= 0) return 0; /* error indication */ # define RPUT(c) if (out) out[len] = c; len++ while(n>= 1000) { n -= 1000;RPUT('M'); }; if (n >= 

900) { n -= 900; RPUT('C'); RPUT('M'); }; if (n >= 500) { n -= 500; RPUT('D'); }; if (n >= 400) { n -= 400; RPUT('C'); RPUT('D'); }; while (n >= 100){ n -= 100; RPUT('C'); }; if (n >= 90) { n -= 90; RPUT('X'); RPUT('C'); }; if (n >= 50) { n -= 50; RPUT('L'); }; if (n >= 40) { n -= 40; RPUT('X'); RPUT('L'); }; while (n >= 10) { n -= 10; RPUT('X'); }; if (n >= 9) { n -= 9; RPUT('I'); RPUT('X'); }; if (n >= 5) { n -= 5; RPUT('V'); }; if (n >= 4) { n -= 4; RPUT('I'); RPUT('V');

 }; while (n) { n--; RPUT('I'); }; printf ("roman for 1666 is %d bytes: %s\n", d, buf); printf ("%d would have required %d bytes\n", d, to_roman(0, d)); class Program { static uint[] nums = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; static string[] rum = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; for (int i = 0; i < nums.Length && number != 0; i++) { for (uint number = 1; number <= 1 << 10; number *= 2) { Console.WriteLine("{0} = {1}", number, ToRoman(number)); } Func<int, string> toRoman = (number) => new Dictionary<int, string> }.Aggregate( new string('I', number), (m

, _) => m.Replace( new string('I', _.Key), _.Value)); Output: 1 = I 2 = II 4 = IV 8 = VIII 16 = XVI 32 = XXXII 64 = LXIV 128 = CXXVIII 256 = CCLVI 512 = DXII 1024 = MXXIV struct romandata_t { int value; char const* numeral; }; static romandata_t const romandata[] = for (romandata_t const* current = romandata; current->value > 0; ++current) { for (int i = 1; i <= 4000; ++i) { class Numeral(shared Character char, shared Integer int) {} [Numeral('I', 1), Numeral('V', 5), Numeral('X', 10)], [Numeral('X', 10), Numeral('L', 50), Numeral('C', 100)], [Numeral('C', 100), Numeral('D', 500), Numeral('M', 1k)] ]; String to

Roman(Integer hindu, Integer power = 2) { assert(exists tier = tiers[power]); } else if(exists num = tier.rest.find((Numeral elem) => elem.int - tier.first.int <= hindu < elem.int)) { return "``tier.first.char````num.char````toRoman(hindu - (num.int - tier.first.int), power)``"; } else if(exists num = tier.reversed.find((Numeral elem) => hindu >= elem.int)) { return "``num.char````toRoman(hindu - num.int, power)``"; } else { The easiest way is to use the built-in cl-format function (def arabic->roman (partial clojure.pprint/cl-format nil " [email protected] ")) 1 "I", 4 "IV", 5 "V", 9 "IX", 10 "X", 40 "XL", 50 "L", 90 "XC", 100 "C", 400 "CD", 500 "D", 900 "CM" 1000 "M")) (loop [res (StringBuilder.), n n] (if-let [

v (roman-map n)] (let [[k v] (->> roman-map keys (filter #(> n %)) last (find roman-map))] (recur (.append res v) (- n k)))))) (let [rv '(1000 500 100 50 10 5 1) rm (zipmap rv "MDCLXVI") dv (->> rv (take-nth 2) next #(interleave % %))] (loop [a a rv rv dv dv r nil] (if (<= a 0) d (or (first dv) 0) l (- v d)] (= a v) (str r (rm v)) (= a l) (str r (rm d) (rm v)) (and (> a v) (> a l)) (recur (- a v) rv dv (str r (rm v))) (and (< a v) (< a l)) (recur a (rest rv) (rest dv) r) :else (recur (- a l) (rest rv) (rest dv) (str r (rm d) (rm v))))))))) (map a2r [1000 1 389 

45]) ("M" "I" "CCCLXXXIX" "XLV") 01 ws-number pic 9(4) value 0.

01 ws-save-number pic 9(4). 03 filler pic x(7) value '1000M '. 03 filler pic x(7) value '0900CM '. 03 filler pic x(7) value '0500D '. 03 filler pic x(7) value '0400CD '. 03 filler pic x(7) value '0100C '. 03 filler pic x(7) value '0090XC '. 03 filler pic x(7) value '0050L '. 03 filler pic x(7) value '0040XL '. 03 filler pic x(7) value '0010X '. 03 filler pic x(7) value '0009IX '. 03 filler pic x(7) value '0005V '. 03 filler pic x(7) value '0004IV '. 03 filler pic x(7) value '0001I '. 01 filler redefines ws-tbl-def. 03 filler occurs 13 times indexed by rx. 05 ws-tbl-divisor pic 9(4).

05 ws-tbl-roman-ch pic x(1) occurs 3 times indexed by cx. 01 ocx pic 99. 03 ws-roman-ch pic x(1) occurs 16 times. PROCEDURE DIVISION.

if ws-number > 0 and ws-number < 4000 initialize ws-roman perform varying rx from 1 by +1 until ws-number = 0 perform until ws-number < ws-tbl-divisor (rx) perform varying cx from 1 by +1 until ws-tbl-roman-ch (rx, cx) = spaces compute ocx = ocx + 1 move ws-tbl-roman-ch (rx, cx) to ws-roman-ch (ocx) end-perform compute ws-number = ws-number - ws-tbl-divisor (rx) end-perform display 'inp=' ws-save-number ' roman=' ws-roman else Output: (input was supplied via STDIN) inp=0111 roman=CXI inp=2234 roman=MMCCXXXIV inp=0501 roman=DI inp=0010 roman=X inp=0040 roman=XL inp=0050 roman=L inp=0066 roman=LXVI inp=0666 roman=DCLXVI inp=5666 invalid inp=3333 roman=MMMCCCXXXIII inp=3888 roman=MMMDCCCLXXXVIII inp

=3999 roman=MMMCMXCIX inp=3345 roman=MMMCCCXLV # This should work for any positive integer, although it # gets a bit preposterous for large numbers.

if n >= 4000 for expected, decimal of tests roman = decimal_to_roman(decimal) console.log "error for #{decimal}: #{roman} is wrong" string toRoman(int n) pure nothrow in { static immutable weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; static immutable symbols = ["M","CM","D","CD","C","XC","L", i: Integer; const WEIGHTS: array[0..12] of Integer = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1); SYMBOLS: array[0..12] of string = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'); begin for i := Low(WEIGHT

S) to High(WEIGHTS) do begin while aValue >= WEIGHTS[i] do begin const weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; const symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]; for i := 0 to weights.High do begin w := weights[i]; while n >= w do begin Result += symbols[i]; if n = 0 then Break; end; SetWeights := [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; SetSymbols := ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']; ProcessRec

 := RECORD dsWeights := DATASET(13,TRANSFORM(ProcessRec,SELF.val := Int, SELF := [])); SymbolStr(i,n,STRING s) := CHOOSE(n+1,'',SetSymbols[i],SetSymbols[i]+SetSymbols[i],SetSymbols[i]+SetSymbols[i]+SetSymbols[i],s); RECORDOF(dsWeights) XF(dsWeights L, dsWeights R, INTEGER C) := TRANSFORM ThisVal := IF(C=1,R.Val,L.Val); SELF.Roman := IF(IsDone,L.Roman,L.Roman + SymbolStr(C,ThisVal DIV SetWeights[C],L.Roman)); SELF.val := IF(IsDone,0,ThisVal - ((ThisVal DIV SetWeights[C])*SetWeights[C])); END; numbers := <<1990, 2008, 1666, 3159, 1977, 2010>> -- "MCMXC", "MMVIII", "MDCLXVI", "MMMCLIX", "MCMLXXVII", "MMX" across numbers as n loop print (n.item.out + " in decimal Arabic numerals is " + decimal_to_roman (n.item) + " in

 Roman numerals.%N") end -- Representation of integer `a_int' as Roman numeral require dnums := <<1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1>> rnums := <<"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I">> i > dnums.count or dnum <= 0 loop digit x y z k = [[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,z]] : (toInt k - 1) toRoman x | x < 0 = fail "Negative roman numeral" | x >= 1000 = 'M' :: toRoman (x - 1000) | x >= 100 = let (q,r) = x `divrem` 100 in digit 'C' 'D' 'M' q ++ toRoman r | x >=

 10 = let (q,r) = x `divrem` 10 in digit 'X' 'L' 'C' q ++ toRoman r | else = digit 'I' 'V' 'X' x def encode(x) when x >= 1000, do: [?M | encode(x - 1000)] def encode(x) when x >= 100, do: digit(div(x,100), ?C, ?D, ?M) ++ encode(rem(x,100)) def encode(x) when x >= 10, do: digit(div(x,10), ?X, ?L, ?C) ++ encode(rem(x,10)) def encode(x) when x >= 1, do: digit(x, ?I, ?V, ?X) defp digit(1, x, _, _), do: [x] defp digit(2, x, _, _), do: [x, x] defp digit(3, x, _, _), do: [x, x, x] defp digit(4, x, y, _), do: [x, y] defp digit(

5, _, y, _), do: [y] defp digit(6, x, y, _), do: [y, x] defp digit(7, x, y, _), do: [y, x, x] defp digit(8, x, y, _), do: [y, x, x, x] defp digit(9, x, _, z), do: [x, z] end @symbols [ {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'} ] def encode(num) do {roman,_} = Enum.reduce(@symbols, {[], num}, fn {divisor, letter}, {memo, n} -> {memo ++ List.duplicate(letter, div(n, divisor)), rem(n, divisor)} end) Enum.each([1990, 2008

, 1666], fn n -> IO.puts "#{n}: #{Roman_numeral.encode(n)}" "translate from arabic number AN to roman number, ar2ro(1666) returns (M D C L X V I)" (cond ((>= AN 1000) (cons 'M (ar2ro (- AN 1000)))) ((>= AN 900) (cons 'C (cons 'M (ar2ro (-AN 900))))) ((>= AN 500) (cons 'D (ar2ro (- AN 500)))) ((>= AN 400) (cons 'C (cons 'D (ar2ro (- AN 400))))) ((>= AN 100) (cons 'C (ar2ro (- AN 100)))) ((>= AN 90) (cons 'X (cons 'C (ar2ro (- AN 90))))) ((>= AN 50) (cons 'L (ar2ro (- AN 50)))) ((>= AN 40) (cons 'X (cons 'L (ar2ro (- AN 40))))) ((

>= AN 10) (cons 'X (ar2ro (- AN 10)))) ((>= AN 5) (cons 'V (ar2ro (- AN 5)))) ((>= AN 4) (cons 'I (cons 'V (ar2ro (- AN 4))))) ((>= AN 1) (cons 'I (ar2ro (- AN 1)))) ((= AN 0) nil))) to_roman(X) when X >= 1000 -> [$M | to_roman(X - 1000)]; to_roman(X) when X >= 100 -> digit(X div 100, $C, $D, $M) ++ to_roman(X rem 100); to_roman(X) when X >= 10 -> digit(X div 10, $X, $L, $C) ++ to_roman(X rem 10); to_roman(X) when X >= 1 -> digit(X, $I, $V, $X).

digit(3, X, _, _) -> [X, X, X]; digit(4, X, Y, _) -> [X, Y]; digit(5, _, Y, _) -> [Y]; digit(6, X, Y, _) -> [Y, X]; digit(7, X, Y, _) -> [Y, X, X]; digit(8, X, Y, _) -> [Y, X, X, X]; digit(9, X, _, Z) -> [X, Z]. sample: 1> c(roman). {ok,roman} 2> roman:to_roman(1999). "MCMXCIX" 3> roman:to_roman(25). "XXV" 4> roman:to_roman(944). "CMXLIV" Alternative: encode_from_integer( N ) when N > 0 -> #encode_acc{romans=Romans} = lists :foldl( fun encode_from_integer/2, #encode_acc{n=N}, map() ), Romans.

encode_from_integer( _Map, #encode_acc{n=0}=Acc ) -> Acc; encode_from_integer( {_Roman, Value}, #encode_acc{n=N}=Acc ) when N < Value -> Acc; encode_from_integer( {Roman, Value}, #encode_acc{n=N, romans=Romans} ) -> Times = N div Value, New_roman = lists :flatten( lists :duplicate(Times, Roman) ), #encode_acc{n=N - (Times * Value), romans=Romans ++ New_roman}. map() -> [{"M",1000}, {"CM",900}, {"D",500}, {"CD",400}, {"C",100}, {"XC",90}, {"L",50}, {"XL",40}, {"X",10}, {"IX",9}, {"V",5}, {"IV",4}, {"I\ ",1}].

DIM ARABIC%[12],ROMAN$[12] FOR I%=0 TO 12 DO WHILE VALUE>=ARABIC%[I%] DO RESULT$+=ROMAN$[I%] constant arabic = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 } constant roman = {"M", "CM", "D","CD", "C","XC","L","XL","X","IX","V","IV","I"} for i = 1 to 13 do while val >= arabic[i] do result &= roman[i] printf(1,"%d = %s\n",{2009,toRoman(2009)}) printf(1,"%d = %s\n",{1666,toRoman(1666)}) printf(1,"%d = %s\n",{3888,toRoman(3888)}) Output: Excel can encode numbers in Roman forms in 5 successively concise forms. These can be indicated from 0 to 4.

Type in a cell: let digit x y z = function 1 -> x | 8 -> y + x + x + x | 9 -> x + z | _ -> failwith "invalid call to digit" let rec to_roman acc = function | x when x >= 1000 -> to_roman (acc + "M") (x - 1000) | x when x >= 100 -> to_roman (acc + digit "C" "D" "M" (x / 100)) (x % 100) | x when x >= 10 -> to_roman (acc + digit "X" "L" "C" (x / 10)) (x % 10) | x when x > 0 -> acc + digit "I" "V" "X" x | 0 -> acc | _ -> failwith "invalid call to_roman (negative input)" let roman n = to_roman "" n |> List .map (fun n -> roman n) |> List .iter (printfn "%s") A roman numeral library ships with Factor.

USE: roman { "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" } { 1000 900 500 400 100 90 50 40 10 9 5 4 1 } dup 1 10000 between?

[ roman-range-error ] unless ; [ /mod swap ] dip <repetition> concat ] 2map "" concat-as nip ; ** converts a number to its roman numeral representation ** private Str digit(Str x, Str y, Str z, Int i) { case 2: return x+x case 3: return x+x+x case 4: return x+y case 5: return y case 6: return y+x case 7: return y+x+x case 8: return y+x+x+x case 9: return x+z } if (i>=1000) { return "M" + toRoman(i-1000) } if (i>=100) { return digit("C", "D", "M", i/100) + toRoman(i%100) } if (i>=10) { return digit("X", "L", "C", i/10) + toRoman(i%10) } if (i>=1) { return digit("I", "V", "X", i) } return "" : vector create ( n -- ) 0 do , loop does> ( n -- ) swap cells + @ execute ; \ these

 are ( numerals -- numerals ) : ,I dup c@ C, ; : ,V dup 1 + c@ C, ; : ,X dup 2 + c@ C, ; :noname ,I ,X drop ; :noname ,V ,I ,I ,I drop ; :noname ,V ,I ,I drop ; :noname ,V ,I drop ; :noname ,V drop ; :noname ,I ,V drop ; :noname ,I ,I ,I drop ; :noname ,I ,I drop ; :noname ,I drop ; ' drop ( 0 : no output ) 10 vector ,digit : roman-rec ( numerals n -- ) 10 /mod dup if >r over 2 + r> recurse else drop then ,digit ; : roman ( n -- c-addr u ) dup 0 4000 within 0= abort" EX LIMITO!" HERE SWAP s" IVXLCDM" drop swap roman-rec HERE OVER - ; create romans 0 , 1 , 5 , 21 , 9 , 2 , 6 , 22 , 86 , 13 , does

> swap cells + @ ; : roman-digit ( a1 n1 a2 n2 -- a3) drop >r romans begin dup while tuck 4 mod 1- chars r@ + c@ over c!

char+ swap 4 / repeat r> drop drop : (split) swap >r /mod r> swap ; : >roman ( n1 a -- a n2) tuck 1000 (split) s" M " roman-digit 100 (split) s" CDM" roman-digit 10 (split) s" XLC" roman-digit 1 (split) s" IVX" roman-digit nip over - ; create (roman) 16 chars allot Works with: Fortran version 90 and later program roman_numerals function roman (n) result (r) integer, dimension (d_max), parameter :: d_dec = & & (/1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1/) character (32), dimension (d_max), parameter :: d_rom = & & (/'M ', 'CM', 'D ', 'CD', 'C ', 'XC', 'L ', 'XL', 'X ', 'IX', 'V ', 'IV', 'I '/) r =

 trim (r) // repeat (trim (d_rom (d)), m_div) m = m - d_dec (d) * m_div end do Function romanEncode(n As Integer) As String If n < 1 OrElse n > 3999 Then Return "" '' can only encode numbers in range 1 to 3999 Dim roman1(0 To 2) As String = {"MMM", "MM", "M"} Dim roman2(0 To 8) As String = {"CM", "DCCC", "DCC", "DC", "D", "CD", "CCC", "CC", "C"} Dim roman3(0 To 8) As String = {"XC", "LXXX", "LXX", "LX", "L", "XL", "XXX", "XX", "X"} Dim roman4(0 To 8) As String = {"IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I"} Dim As Integer thousands, hundreds, tens, units thousands = n \ 1000 If thousands > 0 Then roman += roman1(3 - thousands) If hundreds > 0

 Then roman += roman2(9 - hundreds) If tens > 0 Then roman += roman3(9 - tens) If units > 0 Then roman += roman4(9 - units) Return roman Dim a(2) As Integer = {1990, 2008, 1666} For i As Integer = 0 To 2 Print a(i); " => "; romanEncode(a(i)) Print "Press any key to quit" Sleep local fn DecimaltoRoman( decimal as short ) as Str15 dim as short arabic(12) dim as Str15 result : result = "" arabic(0) = 1000 : arabic(1) = 900 : arabic(2) = 500 : arabic(3) = 400 arabic(4) = 100 : arabic(5) = 90 : arabic(6) = 50 : arabic(7) = 40 arabic(8) = 10 : arabic(9) = 9 : arabic(10) = 5 : arabic(11) = 4: arab

ic(12) = 1 roman(0) = "M" : roman(1) = "CM" : roman(2) = "D" : roman(3) = "CD" roman(4) = "C" : roman(5) = "XC" : roman(6) = "L" : roman(7) = "XL" roman(8) = "X" : roman(9) = "IX" : roman(10) = "V" : roman(11) = "IV" : roman(12) = "I" for i = 0 to 12 while ( decimal >= arabic(i) ) if result == "" then result = "Zepherium" end fn = result print "1990 = "; fn DecimaltoRoman( 1990 ) print "2008 = "; fn DecimaltoRoman( 2008 ) print "2016 = "; fn DecimaltoRoman( 2016 ) print "1666 = "; fn DecimaltoRoman( 1666 ) print "3888 = "; fn DecimaltoRoman( 3888 ) print

 "1914 = "; fn DecimaltoRoman( 1914 ) print "1000 = "; fn DecimaltoRoman( 1000 ) print " 513 = "; fn DecimaltoRoman( 513 ) print " 33 = "; fn DecimaltoRoman( 33 ) Output: 1990 = MCMXC 2008 = MMVIII 2016 = MMXVI 1666 = MDCLXVI 3888 = MMMDCCCLXXXVIII 1914 = MCMXIV 1000 = M 513 = DXIII 33 = XXXIII Go [ edit ] For fluff, the unicode overbar is recognized as a factor of 1000, as described in WP .

If you see boxes in the code below, those are supposed to be the Unicode combining overline (U+0305) and look like IVXLCDM. Or, if you see overstruck combinations of letters, that's a different font rendering problem.

(If you need roman numerals > 3999 reliably, it might best to stick to chiseling them in stone...) package main m0 = []string{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"} m1 = []string{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"} m2 = []string{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"} m3 = []string{"", "M", "MM", "MMM", "I̅V̅", "V̅", "V̅I̅", "V̅I̅I̅", "V̅I̅I̅I̅", "I̅X̅"} m4 = []string{"", "X̅", "X̅X̅", "X̅X̅X̅", "X̅L̅", "L̅", "L�

�X̅", "L̅X̅X̅", "L̅X̅X̅X̅", "X̅C̅"} m5 = []string{"", "C̅", "C̅C̅", "C̅C̅C̅", "C̅D̅", "D̅", "D̅C̅", "D̅C̅C̅", "D̅C̅C̅C̅", "C̅M̅"} m6 = []string{"", "M̅", "M̅M̅", "M̅M̅M̅"} ) func formatRoman(n int) (string, bool) { if n < 1 || n >= 4e6 { return "", false // this is efficient in Go.

the seven operands are evaluated, // then a single allocation is made of the exact size needed for the result.

return m6[n/1e6] + m5[n%1e6/1e5] + m4[n%1e5/1e4] + m3[n%1e4/1e3] + m2[n%1e3/1e2] + m1[n%100/10] + m0[n%10], true // show three numbers mentioned in task descriptions for _, n := range []int{1990, 2008, 1666} { r, ok := formatRoman(n) symbols = [ 1:'I', 4:'IV', 5:'V', 9:'IX', 10:'X', 40:'XL', 50:'L', 90:'XC', 100:'C', 400:'CD', 500:'D', 900:'CM', 1000:'M' ] With an explicit decimal digit representation list: digit x y z k = [[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,z]] !!

toRoman x | x < 0 = error "Negative roman numeral" toRoman x | x >= 1000 = 'M' : toRoman (x - 1000) toRoman x | x >= 100 = digit 'C' 'D' 'M' q ++ toRoman r where (q,r) = x ` divMod ` 100 toRoman x | x >= 10 = digit 'X' 'L' 'C' q ++ toRoman r where (q,r) = x ` divMod ` 10 toRoman x = digit 'I' 'V' 'X' x Output: *Main> map toRoman [1999,25,944] ["MCMXCIX","XXV","CMXLIV"] or, defining roman in terms of mapAccumL import Data.List (mapAccumL) tr a (m, s) = (r, replicate q s) where (q, r) = quotRem a m main = mapM_ ( putStrLn .

roman) [1666, 1990, 2008, 2016, 2017] Output: DATA ddec/1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1/ DO d = 1, 13 DO rep = 1, todo / ddec(d) roman = TRIM(roman) // TRIM(CHAR(d, 13, "M CM D CD C XC L XL X OX V IV I ")) todo = todo - ddec(d) write(commas(x), " -> ",roman(x)|"*** can't convert to Roman numerals ***") end numbers.icn provides roman as seen below and is based upon a James Gimple SNOBOL4 function.

procedure roman(n) #: convert integer to Roman numeral local arabic, result result := map(result,"IVXLCDM","XLCDM**") || equiv[arabic + 1] if find("*",result) then fail else return result end Output: #roman.exe 3 4 8 49 2010 1666 3000 3999 4000 3 -> III 4 -> IV 8 -> VIII 49 -> XLIX 2,010 -> MMX 1,666 -> MDCLXVI 3,999 -> MMMCMXCIX 4,000 -> *** can't convert to Roman numerals *** nums := list(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) rum := list("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I") rfd

 obtains Roman numerals from decimals.

R1000=. ;L:1 ,{ <@(<;._1);._2]0 :0 C CC CCC CD D DC DCC DCCC CM X XX XXX XL L LX LXX LXXX XC I II III IV V VI VII VIII IX ) rfd=: ('M' $~ <.@%&1000) , R1000 {::~ 1000&| Explanation: R1000's definition contains rows representing each of 10 different digits in the 100s, 10s and 1s column (the first entry in each row is blank -- each entry is preceded by a space). R1000 itself represents the first 1000 roman numerals (the cartesian product of these three rows of roman numeral "digits" which is constructed so that they are in numeric order. And the first entry -- zero -- is just blank).

To convert a number to its roman numeral representation, we will separate the number into the integer part after dividing by 1000 (that's the number of 'M's we need) and the remainder after dividing by 1000 (which will be an index into R1000). For example: The conversion function throws an IllegalArgumentException for non-positive numbers, since Java does not have unsigned primitives.

Works with: Java version 1.5+ public class RN { I(1), IV(4), V(5), IX(9), X(10), XL(40), L(50), XC(90), C(100), CD(400), D(500), CM(900), M(1000); int weight; public static String roman(long n) { for (int i = values.length - 1; i >= 0; i--) { while (n >= values[i].weight) { buf.append(values[i]); public static void main( String [] args) { test(1999); } Output: 1999 = MCMXCIX 25 = XXV 944 = CMXLIV Exception in thread "main" java.lang.IllegalArgumentException at RN.roman(RN.java:15) at RN.test(RN.java:31) at RN.main(RN.java:38) Works with: Java version 1.8+ import java.util.Set; M(1000), CM(900), D(500), CD(400), C(100

), XC(90), L(50), XL(40), X(10), IX(9), V(5), IV(4), I(1); private static final Set <Numeral> SET = Collections .unmodifiableSet( EnumSet .allOf(Numeral.class)); public static Numeral getLargest(long weight) { return SET.stream() public static String encode(long n) { return LongStream.iterate(n, l -> l - Numeral.getLargest(l).weight) .limit(Numeral.values().length) public static long decode( String roman) { long result = new StringBuilder (roman.toUpperCase()).reverse().chars() .mapToObj(c -> Character .toString((char) c)) .reduce(0, (a, b) -> a + (a <= b ?

b : -b)) ; result += 2 * Enum .valueOf(Numeral.class, roman.substring(0, 1)).weight; } public static void main( String [] args) { LongStream.of(1999, 25, 944).forEach(RomanNumerals::test); 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 (var idx = 0; n > 0 && idx < this.map.length; idx += 2) { while (n >= this.map[idx]) { // If the Roman is a string, pass any delimiters through dlm = ps.length > 1 ?

ps[1] : undefined; return [[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"]] .reduce(function (a, lstPair) { return [2016, 1990, 2008, "14.09.2015", 2000, 1666].map( romanTranscription); // If the input is a decimal string, pass any delimiters through // romanTranscription :: (Int | String) -> String dlm = ps.length > 1 ?

ps[1] : undefined; return [2016, 1990, 2008, "14.09.2015", 2000, 1666] .map(romanTranscription); const DR = [["I", "X", "C", "M"] ["V", "L", "D", "MMM"]] rnum = "" if n > 4999 || n < 1 throw(DomainError()) for (omag, d) in enumerate(digits(n)) if d == 0 omr = DR[omag, 1]^d elseif d == 4 omr = DR[omag, 1]*DR[omag, 2] elseif d == 5 omr = DR[omag, 2]*(DR[omag, 1]^(d - 5)) else omr = DR[omag, 1]*DR[(omag +1), 1] end testcases = Int64[1990, 2008, 1668] for i in 1:12 Output: Test romanencode, arabic => roman: 

1990 => MCMXC 2008 => MMVIII 1668 => MDCLXVIII 1664 => MDCLXIV 949 => CMXLIX 906 => CMVI 1304 => MCCCIV 1394 => MCCCXCIV 1190 => MCXC 1988 => MCMLXXXVIII 447 => CDXLVII 1542 => MDXLII 4579 => MMMMDLXXIX 115 => CXV 2302 => MMCCCII Standard Roman numerals do not support 0 or negative integers.

Although the integers 5000 and larger can be expressed by adding enough Ms, the standard formulation can not support these larger numbers. I've chosen to throw a DomainError when these standard limits are exceeded.

julia> romanencode(1) "I" julia> romanencode(0) ERROR: DomainError in romanencode at none:5 julia> romanencode(4999) "MMMMCMXCIX" julia> romanencode(5000) ERROR: DomainError in romanencode at none:5 // encode roman define encodeRoman(num::integer)::string => { local(ref = array('M'=1000, 'CM'=900, 'D'=500, 'CD'=400, 'C'=100, 'XC'=90, 'L'=50, 'XL'=40, 'X'=10, 'IX'=9, 'V'=5, 'IV'=4, 'I'=1)) local(out = string) with i in #ref do => { while(#num >= #i->second) => { '1990 in roman is '+encodeRoman(1990) br '2008 in roman is '+encodeRoman(2008) br \newcounter{currentyear}\setcounter{currentyear}{\year} Anno Domini \Roman{currentyear} for i =0

 to 12 read k data 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 for i =0 to 12 read k$ data "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" for i = 0 to 12 while value >=arabic( i) repeat with n = 1 to the number of items of romans put numArabic div item n of arabics into nums if nums > 0 then put repeatChar(item n of romans,nums) after roman add -(nums * item n of arabics) to numArabic end if [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] to roman :n [:rules :roman.rules] [:acc "||] if empty?

:rules [output :acc] if :n < first first :rules [output (roman :n bf :rules :acc)] output (roman :n - first first :rules :rules word :acc last first :rules) end make "patterns [[?] [? ?] [? ? ?] [? ?2] [?2] [?2 ?] [?2 ? ?] [?2 ? ? ?] [?

?3]] output apply (sentence "\( "word (item :d :patterns) "\)) :numerals end output word ifelse :n < 10 ["||] [digits int :n/10 bf bf :numerals] ~ digit modulo :n 10 :numerals end if or :n < 0 :n >= 4000 [output [EX MODVS!]] output digits :n [I V X L C D M] end Dim i As Integer, result As String For i = 0 To 12 Do While value >= arabic(i) {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 _, v in ipairs(romans) do --note that this is -not- ipairs.

val, let = unpack(v) define(`roman',`ifelse(eval($1>=1000),1,`M`'roman(eval($1-1000))', `ifelse(eval($1>=900),1,`CM`'roman(eval($1-900))', > for n in [ 1666, 1990, 2008 ] do printf( "%d\t%s\n", n, convert( n, 'roman' ) ) end: 1666 MDCLXVI RomanNumeral is a built-in function in the Wolfram language Examples: Mercury [ edit ] The non-ceremonial work in this program starts at the function to_roman/1. Unusually for Mercury the function is semi-deterministic. This is because some of the helper functions it calls are also semi-deterministic and the determinism subsystem propagates the status upward. (There are ways to stop it from doing this, but it would distract from the actual Roman numeral conversion process so the semi-determinism has been left in.) to_roman/1 is just a string of chained function calls.

The number is passed in as a string (and the main/2 predicate ensures that it is *only* digits!) is converted into a list of characters. This list is then reversed and the Roman numeral version is built from it. This resulting character list is then converted back into a string and returned. build_roman/1 takes the lead character off the list (reversed numerals) and then recursively calls itself. It uses the promote/2 predicate to multiply the ensuing Roman numerals (if any) by an order of magnitude and converts the single remaining digit to the appropriate list of Roman numerals. To clarify, if it's passed the number "123" (encoded by this point as ['3', '2', '1']) the following transpires: The '3' is removed and build_roman/1 is now called with ['2', '1']. The '2' is removed and the function is recursively called with ['1']. The '1' is removed and the function is recursively called with [] (the empty list).. The function returns []. The [] has its (non-existent) digits promoted and then gets ['I'] appended (1 converts to ['I'] via digit_to_roman/1).

The ['I'] has its (single) digit promoted and is converted to ['X'] and then gets ['I','I'] appended from the 2's conversion. The resulting list is now ['X','I','I'] (or 12). The ['X','I','I'] has all of its digits promoted, yielding ['C','X','X'] before getting ['I','I','I'] appended. The resulting list is now ['C','X','X','I','I','I'] which is converted into the string "CXXIII" back up in to_roman/1. It is possible for this to be implemented differently even keeping the same algorithm. For example the map module from the standard library could be used for looking up conversions and promotions instead of having digit_to_roman/1 and promote. This would require, however, either passing around the conversion tables constantly (bulking up the parameter lists of all functions and predicates) or creating said conversion tables each time at point of use (slowing down the implementation greatly). Now the semi-determinism of the functions involved is a little bit of a problem. In the main/2 predicate you can see one means of dealing with it.

main/2 *must* be deterministic (or cc_multi, but this is equivalent for this discussion). There can be *no* failure in a called function or predicate … unless that failure is explicitly handled somehow. In this implementation the failure is handled in the foldl/4's provided higher-order predicate lambda. The call to to_roman/1 is called within a conditional and both the success (true) and failure (false) branches are handled. This makes the passed-in predicate lambda deterministic, even though the implementation functions and predicates are semi-deterministic. But why are they semi-deterministic? Well, this has to do with the type system. It doesn't permit sub-typing, so when the type of a predicate is, say pred(char, char) (as is the case for promote/2), the underlying implementation *must* handle *all* values that a type char could possibly hold. It is trivial to see that our code does not. This means that, in theory, it is possible that promote/2 (or digit_to_roman/1) could be passed a value which cannot be processed, thus triggering a false result, and thus being semi-deterministic.

Question: What number is produced by displaying one of each of the Roman numerals, in descending order?
Answer: 1666