31
\$\begingroup\$

Write a program that count from 1 to 100 in Roman Numerals and print these numbers by standard output. Each one of the numbers must be separated by spaces.

You cannot use any built in function to transform to roman numerals nor external application or library to do so.

The desired result is

I II III IV V VI VII VIII IX X XI XII XIII XIV XV XVI XVII XVIII XIX XX XXI XXII XXIII XXIV XXV XXVI XXVII XXVIII XXIX XXX XXXI XXXII XXXIII XXXIV XXXV XXXVI XXXVII XXXVIII XXXIX XL XLI XLII XLIII XLIV XLV XLVI XLVII XLVIII XLIX L LI LII LIII LIV LV LVI LVII LVIII LIX LX LXI LXII LXIII LXIV LXV LXVI LXVII LXVIII LXIX LXX LXXI LXXII LXXIII LXXIV LXXV LXXVI LXXVII LXXVIII LXXIX LXXX LXXXI LXXXII LXXXIII LXXXIV LXXXV LXXXVI LXXXVII LXXXVIII LXXXIX XC XCI XCII XCIII XCIV XCV XCVI XCVII XCVIII XCIX C

As it is a code golf challenge, shortest code wins.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ 39 is missing an X. \$\endgroup\$
    – Thor
    Dec 14, 2012 at 13:02
  • 1
    \$\begingroup\$ I really want to use INTERCAL for this one. \$\endgroup\$ Mar 19, 2018 at 18:36
  • \$\begingroup\$ can it be separated by newlines? Also what about trailing/leading spaces/newlines? \$\endgroup\$
    – qqq
    Mar 19, 2018 at 20:14

22 Answers 22

77
\$\begingroup\$

Perl 69 bytes

s;.;y/XVI60-9/CLXVIX/dfor$a[$_].="32e$&"%72726;gefor 1..100;print"@a"

Works by way of magic formula. The expression "32e$&"%72726 transforms each digit in the following manner:
0⇒32, 1⇒320, 2⇒3200, 3⇒32000, 4⇒29096, 5⇒56, 6⇒560, 7⇒5600, 8⇒56000, 9⇒50918

After applying the translation y/016/IXV/, we have this instead:
0⇒32, 1⇒32I, 2⇒32II, 3⇒32III, 4⇒29I9V, 5⇒5V, 6⇒5VI, 7⇒5VII, 8⇒5VIII, 9⇒5I9X8

The rest of the digits (2-57-9) are removed. Note that this could be improved by one byte by using a formula which translates 012 instead of 016, simplifying /XVI60-9/ to /XVI0-9/. I wasn't able to find one, but maybe you'll have better luck.

Once one digit has been transformed in this manner, the process repeats for the next digit, appending the result, and translating the previous XVIs to CLX at the same time the translation for the new digit occurs.

Update
Exhaustive search didn't reveal anything shorter. I did, however, find an alternative 69 byte solution:

s;.;y/XVI0-9/CLXIXV/dfor$a[$_].="57e$&"%474976;gefor 1..100;print"@a"

This one uses a 0-2 substitution for IXV, but has a modulo that's one digit longer.


Update: 66 65 bytes

This version is notably different, so I should probably say a few words about it. The formula it uses is actually one byte longer!

Unable to shorten the formula any more than it is, I decided to golf down what I had. It wasn't long until I remembered my old friend $\. When a print statment is issued, $\ is automatically appended to the end of the output. I was able to get rid of the awkward $a[$_] construction for a two byte improvement:

s;.;y/XVI60-9/CLXVIX/dfor$\.="32e$&"%72726;ge,$\=!print$"for 1..100

Much better, but that $\=!print$" still looked a bit verbose. I then remembered an alternative, equal length formula I had found which didn't contain the number 3 in any of its digit transforms. So, it should be possible to use $\=2+print instead, and substitute the resulting 3 with a space:

s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;ge,$\=2+print for 1..100

Also 67 bytes, due to the necessary whitespace between print and for.

Edit: This can be improved by one byte, by moving the print to the front:

$\=2+print!s;.;y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535;gefor 1..100

Because the substitution needs to evaluate completely before the print, the assignment to $\ will still occur last. Removing the whitespace between ge and for will issue a deprecation warning, but is otherwise valid.

But, if there were a formula which didn't use a 1 anywhere, $\=2+print becomes $\=print for another two bytes worth of savings. Even if it were one byte longer, it'd still be an improvement.

As it turns out, such a formula does exist, but it is one byte longer than the original, resulting in a final score of 65 bytes:

$\=print!s;.;y/XVI60-9/CLXXI V/dfor$\.="37e$&"%97366;gefor 1..100

Methodology

The question was asked how one might go about finding such a formula. In general, finding a magic formula to generalize any set of data is matter of probability. That is, you want to choose a form which is as likely as possible to produce something similar to the desired result.

Examing the first few roman numerals:

0:
1: I
2: II
3: III
4: IV
5: V
6: VI
7: VII
8: VIII
9: IX

there is some regularity to be seen. Specifically, from 0-3 and then again from 5-8, each successive term increases in length by one numeral. If we wanted to create a mapping from digits to numerals, we would want to have an expression that also increases in length by one digit for each successive term. A logical choice is k • 10d where d is the corresponding digit, and k is any integer constant.

This works for 0-3, but 4 needs to break the pattern. What we can do here is tack on a modulo:
k • 10d % m, where m is somewhere between k • 103 and k • 104. This will leave the range 0-3 untouched, and modify 4 such that it won't contain four Is. If we additionally constrain our search algorithm such that the modular residue of 5, let's call it j, is less than m / 1000, this will ensure that we also have regularity from 5-8 as well. The result is something like this:

0: k
1: k0
2: k00
3: k000
4: ????
5: j
6: j0
7: j00
8: j000
9: ????

As you can see, if we replace 0 with I, 0-3 and 5-8 are all guaranteed to be mapped correctly! The values for 4 and 9 need to be brute forced though. Specifically, 4 needs to contain one 0 and one j (in that order), and 9 needs to contain one 0, followed by one other digit that doesn't appear anywhere else. Certainly, there are a number of other formulae, which by some fluke of a coincidence might produce the desired result. Some of them may even be shorter. But I don't think there are any which are as likely to succeed as this one.

I also experimented with multiple replacements for I and/or V with some success. But alas, nothing shorter than what I already had. Here is a list of the shortest solutions I found (the number of solutions 1-2 bytes heavier are too many to list):

y/XVI60-9/CLXVIX/dfor$\.="32e$&"%72726
y/XVI0-9/CLXIXV/dfor$\.="57e$&"%474976
y/XVI0-9/CLXIVXI/dfor$\.="49e$&"%87971

y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%10606  #
y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%15909  # These are all essentially the same
y/XVI0-9/CLXIIXIV/dfor$\.="7e$&"%31818  #

y/XVI0-9/CLXIIX V/dfor$\.="8e$&"%61535  # Doesn't contain 3 anywhere

y/XVI60-9/CLXXI V/dfor$\.="37e$&"%97366 # Doesn't contain 1 anywhere
\$\endgroup\$
2
  • 3
    \$\begingroup\$ How did you find the magic formula? \$\endgroup\$ Dec 18, 2012 at 20:27
  • 1
    \$\begingroup\$ @RubenVerborgh I'll update my post with more information regarding the methodology soon. \$\endgroup\$
    – primo
    Dec 18, 2012 at 21:13
15
\$\begingroup\$

HTML + JavaScript + CSS (137)

HTML (9)

<ol></ol>

JavaScript (101)

for(i=1;i<=100;i++){document.getElementsByTagName('ol')[0].appendChild(document.createElement('li'))}

CSS (27)

ol{list-style:upper-roman​}

Output

Numbered list with roman numerals

...

Demo on JSBin

\$\endgroup\$
3
  • 1
    \$\begingroup\$ 81 byte JS-only version: document.write('<ol>'+"<li style='list-style:upper-roman'/>".repeat(100)+'</ol>') (ES6) \$\endgroup\$
    – Paperjam
    Sep 9, 2016 at 22:13
  • \$\begingroup\$ or 66 in Chrome document.write("<li style='list-style:upper-roman'/>".repeat(100)) \$\endgroup\$
    – Slai
    Aug 7, 2018 at 23:56
  • \$\begingroup\$ 63 if we omit attribute quotes and the slash: document.write("<li style=list-style:upper-roman>".repeat(100)) \$\endgroup\$ Apr 15, 2022 at 15:28
10
\$\begingroup\$

Python 116

better golfed code of scleaver's answer:

r=lambda a,b,c:('',a,2*a,3*a,a+b,b,b+a,b+a+a,b+3*a,a+c);print' '.join(i+j for i in r(*'XLC')for j in r(*'IVX'))+' C'
\$\endgroup\$
8
\$\begingroup\$

Python, 139

print' '.join(' '.join(i+j for  j in ' _I_II_III_IV_V_VI_VII_VIII_IX'.split('_'))for i in ' _X_XX_XXX_XL_L_LX_LXX_LXXX_XC'.split('_'))+' C'
\$\endgroup\$
6
\$\begingroup\$

C, 177 160 147 chars

There are shorter solutions, but none in C, so here's my try.

New solution, completely different from my previous one:

char*c;
f(n){
    printf("%.*s",n%5>3?2:n%5+n/5,c+=n%5>3?n%4*4:2-n/5);
}
main(i){
        for(;i<100;putchar(32))
                c="XLXXXC",f(i/10),
                c="IVIIIX",f(i++%10);
        puts("C");
}

Previous solution (160 chars):

Logic:
1. f prints a number from 1 to 10. c is the digits used, which can be IVX or XLC. Called once for the tens once for the ones.
2. If n%5==0 - print nothing or c[n/5] which is I or V (or L or C).
3. If n%4==4 - 4 or 9 - print I (or X), by n+1.
4. If n>4 - print 5 (i.e. V or L) then n-5.
5. If n<4 - print I then n-1 (i.e. n times I).

char*c;
p(c){putchar(c);}
f(n){
        n%5?
                n%5>3?
                        f(1),f(n+1):
                        n>4?
                                f(5),f(n-5):
                                f(n-1,p(*c)):
                n&&p(c[n/5]);
}
main(i){
        for(;++i<101;p(32))
                c="XLC",f(i/10),
                c="IVX",f(i%10);
        p(10);
}
\$\endgroup\$
1
  • \$\begingroup\$ 137: f(c,n){printf("%.*s",n%5>3?2:n%5+n/5,"XLXXXCIVIIIX "+c+(n%5>3?n%4*4:2-n/5));}main(i){for(;i<100;f(12,4))f(0,i/10),f(6,i++%10);puts("C");} \$\endgroup\$
    – gastropner
    Jan 28, 2018 at 8:44
5
\$\begingroup\$

Mathematica 159 150 142

c = {100, 90, 50, 40, 10, 9, 5, 4, 1};
Table["" <> Flatten[ConstantArray @@@ Thread@{StringSplit@"C XC L XL X IX V IV I", 
  FoldList[Mod, k, Most@c]~Quotient~c}], {k, 100}]

roman numerals


Built-in solution: IntegerString, 38 chars

IntegerString[k, "Roman"]~Table~{k, 100}
\$\endgroup\$
5
\$\begingroup\$

JavaScript, 123

Inspired by a longer version I came across in a Polish newsgroup (at least, Chrome thought it was Polish).

for(i=100,a=[];n=i--;a[i]=r)
  for(r=y='',x=5;n;y++,x^=7)
    for(m=n%x,n=n/x^0;m--;)
      r='IVXLC'[m>2?y+n-(n&=-2)+(m=1):+y]+r;
alert(a)
\$\endgroup\$
5
\$\begingroup\$

Q (81 80)

2nd cut:

1_,/'[($)``X`XX`XXX`XL`L`LX`LXX`LXXX`XC cross``I`II`III`IV`V`VI`VII`VIII`IX],"C"

1st cut:

1_,/'[$:[``X`XX`XXX`XL`L`LX`LXX`LXXX`XC cross``I`II`III`IV`V`VI`VII`VIII`IX]],"C"
\$\endgroup\$
4
\$\begingroup\$

Python, 168

r=lambda n,l,v:(r(n,l[1:],v[1:])if n<v[0]else l[0]+r(n-v[0],l,v))if n else''
for i in range(1,101):print r(i,'C XC L XL X IX V IV I'.split(),[100,90,50,40,10,9,5,4,1]),

Explanation

Using these values, take the largest value not greater than n and subtract it from n. Repeat until n is 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
\$\endgroup\$
1
  • 1
    \$\begingroup\$ r=lambda n,l,v:n and(n<v[0]and r(n,l[1:],v[1:])or l[0]+r(n-v[0],l,v))or"" saves two characters. Otherwise very nice. \$\endgroup\$
    – cemper93
    Dec 14, 2012 at 21:14
4
\$\begingroup\$

Ruby 1.9, 140 132

r=" "
100.times{r+=?I
0while[[?I*4,"IV"],["VIV","IX"],[?X*4,"XL"],["LXL","XC"],[/(.)((?!\1)[^I])\1/,'\2']].any?{|q|r.sub! *q}
$><<r}

This literally counts from 1 to 100 in Roman numerals. Starts with a blank string, then loops through appending "I" and then repeatedly applying a series of substitution rules, effectively adding 1.

Edit: Added version number, since ?I only works in 1.9, and used @Howard's changes to trim some characters.

\$\endgroup\$
2
  • \$\begingroup\$ You may save two chars: r while -> 0while, r.sub!(*q) -> r.sub! *q. You can also drag the print inside the loop and use 100.times{...} instead of the map statement. \$\endgroup\$
    – Howard
    Dec 14, 2012 at 18:52
  • \$\begingroup\$ (%w[IIII VIV XXXX LXL]<</(.)((?!\1)[^I])\1/).zip(%w(IV IX XL XC)<<'\2') saves 7 chars. \$\endgroup\$
    – steenslag
    Dec 14, 2012 at 23:06
4
\$\begingroup\$

Ruby 112 chars

101.times{|n|r=' ';[100,90,50,40,10,9,5,4,1].zip(%w(C XC L XL X IX V IV I)){|(k,v)|a,n=n.divmod k;r<<v*a};$><<r}

Basically using the to_roman method explained here, but using a zipped array for brevity.

\$\endgroup\$
2
\$\begingroup\$

perl 205

@r = split //, "IVXLC";
@n = (1, 5, 10, 50, 100);

for $num (1..100) {
  for($i=@r-1; $i>=0; $i--) {
    $d = int($num / $n[$i]);
    next if not $d;
    $_ .= $r[$i] x $d;
    $num -= $d * $n[$i];
  }
  $_ .= " ";
}
s/LXXXX/XC/g;
s/XXXX/XL/g;
s/VIIII/IX/g;
s/IIII/IV/g;
print;

Golfed:

@r=split//,"IVXLC";@n=(1,5,10,50,100);for$num(1..100){for($i=@r-1;$i>=0;$i--){$d=int($num/$n[$i]);next if!$d;$_.=$r[$i]x$d;$num-=$d*$n[$i];}$_.=" ";}s/LXXXX/XC/g;s/XXXX/XL/g;s/VIIII/IX/g;s/IIII/IV/g;print;
\$\endgroup\$
2
\$\begingroup\$

MUMPS 184

S V(100)="C",V(90)="XC",V(50)="L",V(40)="XL",V(10)="X",V(9)="IX",V(5)="V",V(4)="IV",V(1)="I" F I=1:1:100 S S=I,N="" F  Q:'S  S N=$O(V(N),-1) I S&(S'<N ) S S=S-N W V(N) S N="" w:'S " "

Same algorithm as @cardboard_box, from whom I've taken the explanation verbatim -

Explanation

Using these values, take the largest value not greater than n and subtract it from n. Repeat until n is 0.

'C'  = 100
'XC' = 90
'L'  = 50
'XL' = 40
'X'  = 10
'IX' = 9
'V'  = 5
'IV' = 4
'I'  = 1
\$\endgroup\$
2
\$\begingroup\$

R, 85 bytes

R=.romans
for(r in 1:100){while(r>0){cat(names(R[I<-R<=r][1]))
r=r-R[I][1]}
cat(" ")}

Try it online!

Uses the random utils package variable .romans to get the values of the roman numerals, but does the conversion by itself; the builtin approach would be 20 bytes: cat(as.roman(1:100))

\$\endgroup\$
2
  • \$\begingroup\$ Surprisingly, the built-in approach you are mentioning does not work as is... one has to type cat(paste(as.roman(1:100))) or simply as.roman(1:100). Weird. \$\endgroup\$
    – JayCe
    Aug 27, 2018 at 19:43
  • \$\begingroup\$ @JayCe odd; I must not have actually tested it...the docs for cat indicate that it performs less conversion than print and only works on atomic vectors -- so that explains this! \$\endgroup\$
    – Giuseppe
    Aug 27, 2018 at 19:51
2
\$\begingroup\$

APL (Dyalog Unicode), 61 bytes

∊{'IVXLC'[∊5 3 1+{(⍺=0)↓⍺↑⍨⊃¯2,⍨⍵~0}⌿0 5⊤1+⍵⊤⍨3⍴10],' '}¨⍳100

Try it online!

(I know there's already an APL answer, but wanted to re-solve it anyway)

Adapted and golfed from Nikolay Nikolov's piece of code that supports up to M at the bottom of this page.

How it works

∊{'IVXLC'[∊5 3 1+{(⍺=0)↓⍺↑⍨⊃¯2,⍨⍵~0}⌿0 5⊤1+⍵⊤⍨3⍴10],' '}¨⍳100
∊{'IVXLC'[...],' '}¨⍳100  ⍝ Less interesting part
                    ⍳100  ⍝ 1-based range; 1 to 100 inclusive
 {        ...     }¨      ⍝ Compute a representation for each number
  'IVXLC'[   ],' '        ⍝ Index into the string and pad with a space
∊                         ⍝ Concatenate the strings

∊5 3 1+{(⍺=0)↓⍺↑⍨⊃¯2,⍨⍵~0}⌿0 5⊤1+⍵⊤⍨3⍴10  ⍝ Interesting part
                                 ⍵⊤⍨3⍴10  ⍝ Last 3 decimal digits of ⍵
                           0 5⊤1+  ⍝ Divmod of each digit +1 by 5
       {                 }⌿  ⍝ Compute the partial roman representation:
⍝  Digit  0  1   2     3       4
⍝  Div    0  0   0     0       1
⍝  Mod    1  2   3     4       0
⍝  Result [] [0] [0 0] [0 0 0] [0 1]
⍝  Digit  5   6     7       8         9
⍝  Div    1   1     1       1         2
⍝  Mod    1   2     3       4         0
⍝  Result [1] [1 0] [1 0 0] [1 0 0 0] [0 2]
                 ⊃¯2,⍨⍵~0  ⍝ ¯2 if mod is 0, keep mod otherwise
              ⍺↑⍨          ⍝ Overtake div with the above
                           ⍝ (¯2: add 0 to the left; other: add (mod-1) 0s to the right)
        (⍺=0)↓             ⍝ Drop one if div is 0
 5 3 1+  ⍝ Offset each nested array to point to correct roman unit
∊        ⍝ Flatten

OP said "You cannot use any built in function to transform to roman numerals" but didn't say about a built-in that does arithmetic over roman numerals, so this one could be technically valid:

APL (Dyalog Unicode), 29 bytes

⎕CY'dfns'
∊,∘' '¨{⍳⍵}roman'C'

Try it online!

This one leverages the library operator roman that does various arithmetic over roman numerals (that is, the input is also a roman numeral). So this basically computes the 1-based range {⍳⍵} over roman numerals with the input value 'C' (100), appends a space to each string ,∘' '¨ and flattens them .

\$\endgroup\$
1
1
\$\begingroup\$

APL 128

I tried an indexing solution in APL:

r←⍬                                                                             
i←1                                                      
l:r←r,' ',('   CXI LV CX'[,⍉((1+(4 4 2 2⊤0 16 20 22 24 32 36 38 39 28)[;1+(3⍴10)⊤i])×3)-4 3⍴2 1 0])~' '
→(100≥i←i+1)/l                                                                  
r              

It can be 4 bytes shorter in index origin 0 instead of 1 but the real space hog is the generation of the index matrix via:

4 4 2 2⊤0 16 20 22 24 32 36 38 39 28

So far I have not been able to generate the indices on the fly!

\$\endgroup\$
1
\$\begingroup\$

LaTeX (138)

\documentclass{minimal}
\usepackage{forloop}
\begin{document}
\newcounter{i}
\forloop{i}{1}{\value{i} < 101}{\roman{i}\par}
\end{document}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ -1: the question says "You cannot use any built in function to transform to roman numerals" \$\endgroup\$
    – izabera
    Sep 4, 2014 at 13:59
1
\$\begingroup\$

Python, 125

' '.join(i+j for i in['']+'X XX XXX XL L LX LXX LXXX XC C'.split()for j in['']+'I II III IV V VI VII VIII IX'.split())[1:-38]
\$\endgroup\$
1
\$\begingroup\$

PHP, 38 37 bytes

<ol type=I><?=str_repeat("<li>",100);

-1 byte thanks to @manatwork

Same idea as Patrick's answer, but in a more compact language. Beats Mathematica!

Try it Online!

\$\endgroup\$
1
  • \$\begingroup\$ Terminate the statement with ;, then no need for ?>. \$\endgroup\$
    – manatwork
    Mar 19, 2018 at 18:34
1
\$\begingroup\$

VBA (Excel), 245 bytes

created Function for Repetition and Replace - 91 bytes

Function s(a,b):s=String(a,b):End Function Function b(x,y,z):b=Replace(x,y,z):End Function

using immediate window (154 bytes)

p="I":for x=1to 100:?b(b(b(b(b(b(b(b(s(x,p),s(100,p),"C"),s(90,p),"XC"),s(50,p),"L"),s(40,p),"XL"),s(10,p),"X"),s(9,p),"IX"),s(5,p),"V"),s(4,p),"IV"):next

\$\endgroup\$
0
\$\begingroup\$

Java (OpenJDK 8), 152 bytes

a->{String[] t=",X,XX,XXX,XL,L,LX,LXX,LXXX,XC,,I,II,III,IV,V,VI,VII,VIII,IX".split(",");for(int i=1;i<100;i++){a+=t[i/10]+t[i%10+10]+" ";}return a+"C";}

Try it online!

Explanation:

String[] t=",X,XX,XXX,XL,L,LX,LXX,LXXX,XC,,I,II,III,IV,V,VI,VII,VIII,IX".split(",");
//Create an array of numerals, first half represents tens place, second half represents ones place
    for(int i=1;i<100;i++){             
//Loop 99 times
        a+=t[i/10]+t[i%10+10]+" ";   
//Add tens place and ones place to the string
    }return a+"C";                         
//Add numeral for 100 and return the string
\$\endgroup\$
1
0
\$\begingroup\$

TeX, 354 bytes

\let~\let~\d\def~\a\advance~\b\divide~\x\expandafter~\f\ifnum{~~\newcount~\n~\i~\j~\k~\u~\v}~~\or\d\p#1{\ifcase#1C~2~L~5~X~2~V~5~I\fi}\d\q#1{\p{#1~}}\d\r{\j0
\v100\d\m{\d\w{\f\n<\v\else\p\j\a\n-\v\x\w\fi}\w\f\n>0\k\j\u\v\d\g{\a\k2\b\u\q\k}\g\f\q\k=2\g\fi\a\n\u\f\n<\v\a\n-\u\a\j2\b\v\q\j\else\p\k\fi\x\m\fi}\m}\i1\d\c{
\f\i<101 \n\i\r\a\i1 \x\c\fi}\c\bye

Some explanation: TeX provides a built-in command \romannumeral for converting numbers to Roman numerals. As the question doesn't allow to use built-in functions, the above code is a golfed version of the same algorithm Knuth's original TeX compiler uses for \romannumeral (see TeX: The Program, § 69, print_roman_int) re-implemented in TeX.

As he wants to leave the joy of puzzling out how this code works to the reader, Knuth refuses to give an explanation of this part of the code. So I'll follow suit and just give an ungolfed and slightly modified version, which is closer to the original than the above code:

\newcount\n
\newcount\j
\newcount\k
\newcount\u
\newcount\v

\def\chrnum#1{\ifcase#1m\or 2\or d\or 5\or c\or 2\or l\or 5\or x\or 2\or v\or 5\or i\fi}
\def\chrnumM#1{\chrnum{#1\or}}

\def\roman#1{%
    \n=#1\relax
    \j=0\relax
    \v=1000\relax
    \def\mainloop{%
        \def\while{%
            \ifnum\n<\v
            \else
                \chrnum\j
                \advance\n -\v
                \expandafter\while
            \fi
        }\while
        \ifnum\n>0\relax
            \k=\j \advance\k 2\relax
            \u=\v \divide\u \chrnumM\k
            \ifnum\chrnumM\k=2\relax
                \advance\k 2\relax
                \divide\u \chrnumM\k
            \fi
            \advance\n \u
            \ifnum\n<\v
                \advance\n -\u
                \advance\j 2\relax
                \divide\v \chrnumM\j
            \else
                \chrnum\k
            \fi
            \expandafter\mainloop
        \fi
    }\mainloop
}

\newcount\i \i=1
\def\countloop{%
    \ifnum\i<100\relax
        \roman\i\ 
        \advance\i 1
        \expandafter\countloop
    \fi
}\countloop
\bye
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.