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


roman = cluster is encode
    rep = null
 
    dmap = struct[v: int, s: string]
    darr = array[dmap]
    own chunks: darr := darr$
       [dmap${v: 1000, s: "M"},
        dmap${v:  900, s: "CM"},
        dmap${v:  500, s: "D"},
        dmap${v:  400, s: "CD"},
        dmap${v:  100, s: "C"},
        dmap${v:   90, s: "XC"},
        dmap${v:   50, s: "L"},
        dmap${v:   40, s: "XL"},
        dmap${v:   10, s: "X"},
        dmap${v:    9, s: "IX"},
        dmap${v:    5, s: "V"},
        dmap${v:    4, s: "IV"},
        dmap${v:    1, s: "I"}]
 
    largest_chunk = proc (i: int) returns (int, string)
        for chunk: dmap in darr$elements(chunks) do
            if chunk.v <= i then return (chunk.v, chunk.s) end
        end            
        return (0, "")
    end largest_chunk
 
    encode = proc (i: int) returns (string)
        result: string := ""
        while i > 0 do
            val: int chunk: string
            val, chunk := largest_chunk(i)
            result := result || chunk
            i := i - val
        end
        return (result)
    end encode 
end roman
 
start_up = proc ()
    po: stream := stream$primary_output()
    tests: array[int] := array[int]$[1666, 2008, 1001, 1999, 3888, 2021]
 
    for test: int in array[int]$elements(tests) do
        stream$putl(po, int$unparse(test) || " = " || roman$encode(test))
    end
end start_up