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


include "cowgol.coh";
include "argv.coh";
 
# Encode the given number as a Roman numeral
sub decimalToRoman(num: uint16, buf: [uint8]): (rslt: [uint8]) is
    # return the start of the buffer for easy printing
    rslt := buf;
 
    # Add string to buffer
    sub Add(str: [uint8]) is
        while [str] != 0 loop
            [buf] := [str];
            buf := @next buf;
            str := @next str;
        end loop;
    end sub;
 
    # Table of Roman numerals
    record Roman is
        value: uint16;
        string: [uint8];
    end record;
 
    var numerals: 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"}
    };
 
    var curNum := &numerals as [Roman];
    while num != 0 loop
        while num >= curNum.value loop
            Add(curNum.string);
            num := num - curNum.value;
        end loop;
        curNum := @next curNum;
    end loop;
 
    [buf] := 0; # terminate the string
end sub;
 
# Read numbers from the command line and print the corresponding Roman numerals
ArgvInit();
var buffer: uint8[100];
loop
    var argmt := ArgvNext();
    if argmt == (0 as [uint8]) then
        break;
    end if;
 
    var dummy: [uint8];
    var number: int32;
    (number, dummy) := AToI(argmt);
 
    print(decimalToRoman(number as uint16, &buffer as [uint8]));
    print_nl();
end loop;