[FUN] Numbers to Roman letters/num

Heyas

Just a little fun script (code block) i'd like to share for fun.

#/bin/bash
# roman.sh
#
#	Function
#
	num2roman() { # NUM
	# Returns NUM in roman letters
	#
		input=$1	# input num
		output=""	# Clear output string
		len=${#input}	# Initial length to count down
		
		roman_val() { # NUM one five ten
		# This sub does the basic 'roman' algorythm
		#
			N=$1
			one=$2
			five=$3
			ten=$4
			out=""
			
			case $N in
			0)	out+=""	;;
			[123])	while [[ $N -gt 0 ]]
				do	out+="$one"
					N=$(($N-1))
				done
				;;
			4)	out+="$one$five"	;;
			5)	out+="$five"	;;
			[678])	out+="$five"
				N=$(($N-5))
				while [[ $N -gt 0 ]]
				do	out+="$one"
					N=$(($N-1))
				done
				;;
			9)	while [[ $N -lt 10 ]]
				do	out+="$one"
					N=$(($N+1))
				done
				out+="$ten"
				;;
			esac
			echo $out
		}
		
		while [[ $len -gt 0  ]]
		do	# There are letters to add
			num=${input:0:1}
			# Do action according position
			case $len in
			1)	# 1
				output+="$(roman_val $num I V X)"
				;;
			2)	# 10
				output+="$(roman_val $num X L C)"
				;;
			3)	# 100
				output+="$(roman_val $num C D M)"
				;;
			*)	# 1000+
				# 10'000 gets a line above, 100'000 gets a line on the left.. how to?
				num=${input:0:(-3)}
				while [[ $num -gt 0 ]]
				do	output+="M"
					num=$(($num-1))
				done
				
				;;
			esac
			input=${input:1} ; len=${#input}
		done
		echo $output
	}
#
#	Call it
#
	num2roman $1

Output:

+ ~ $ for N in 1 4 5 6 8 9 10 13 42 99 123 256 1024 2048 3999;do ./roman.sh $N;done

I
IV
V
VI
VIII
IX
X
XIII
XLII
XCIX
CXXIII
CCLVI
MXXIV
MMXLVIII
MMMCMXCIX

Hth & have fun :slight_smile:

6 Likes

Thank you sea for nice code :b:, here is an awk form of same. Honestly speaking got the idea from your code and tried to make it in awk ,
lets have little more fun.

awk 'BEGIN {INPUT_NUMBERS = split("1990 2008 1666",arr," ")
        for (i=1; i<=INPUT_NUMBERS; i++) {
                                                X = arr
                                                printf("%s = %s\n",X,ROMAN(X))
                                         }
        exit(0)
           }
function ROMAN(number,Z,Y,A,B,rom1,rom10,rom100,rom1000) {
                                                                number = int(number)
                                                                        if (number < 1 || number > 3999)
                                                                        {
                                                                                return
                                                                        }
                                                                        split("I II III IV V VI VII VIII IX",rom1," ")
                                                                        split("X XX XXX XL L LX LXX LXXX XC",rom10," ")
                                                                        split("C CC CCC CD D DC DCC DCCC CM",rom100," ")
                                                                        split("M MM MMM",rom1000," ")
                                                                        Z = (number - (number % 1000)) / 1000
                                                                        number = number % 1000
                                                                        Y = (number - (number % 100)) / 100
                                                                        number = number % 100
                                                                        A = (number - (number % 10)) / 10
                                                                        B = number % 10
                                                                        return(rom1000[Z] rom100[Y] rom10[A] rom1)
                                                         }'
 

Output will be as follows.

1990 = MCMXC
2008 = MMVIII
1666 = MDCLXVI

Thanks,
R. Singh

1 Like

Now it handles numbers up to 99999 :smiley:
4000 in roman numerals please helped me a bit, probaly i just overread the related parts on the wiki page :stuck_out_tongue:
Added:

		U="\033[4m"	# Underscore, multiplies with 1000
		R="\033[0m"	# Resets the underscore

and changed to:

			4)	output+="$(roman_val $num M ${U}V${R} ${U}X${R})"	;;
			5)	output+="$(roman_val $num ${U}X${R} ${U}L${R} ${U}C${R})"	;;
			*)	# Fallback 'failsafe'.. actualy just beeing lazy to handle higher numbers properly... 99999 is enough
				num=${input:0:(-3)}
				while [[ $num -gt 0 ]]
				do	output+="M"
					num=$(($num-1))
				done
				;;
			esac

So the output is now:

~/roman.sh 3999 4500 11600 99000

MMMCMXCIX
MVD
XMDC
XC

Have a nice evening :slight_smile:

Terrific...
Just one point, you are underscoring the multipy by 1000.
It should be an over bar.
Have a look at the upside down capital "C" which can be displayed as a ")" in text mode too, you might find it interesting.
I once wrote an basic article about Roman Numbers many years ago for
The Crypt Magazine

EDIT:
Hmmm, looks like the mag is now closed...

Very interesting thread, well done.

The link is working and i could find most of the issues, but weren't able to locate your article as there is no directory. Would you care to tell us which issue it is in?

bakunin

Hi bakunin...

Crikey this was years ago so no I can't. I do have a copy though on my A1200 I will dig it out and post it if you want to see it...

Cool, I did a roman -> int awk function in this thread

awk '
BEGIN {
    rn="MDCLXVI" # Roman numerals desc order
    split("1000 500 100 50 10 5 1",v) # value for each numeral
}
function roman_val(s,val,c,d,p,q) {
    if(s !~ "^[" rn tolower(rn) "]+$") return 0;
    c = split(toupper(s),d,"")
    val = v[index(rn,d[c])]
    while (--c) {
      p = index(rn,d[c])
      q = index(rn,d[c+1])
      val += (p>q)? -v[p] : v[p]
    }
    return val
}
{ print roman_val($1) }'
$ printf "%s\n" MMXIV XII XXVIII | ./roman.awk
2014
12
28

Here you go...

The short basic article as uploaded many years ago...

Bazza...

2 Likes

True that.
But since underscore is not used by romans otherwise, and you dont mention it either in your article, and upperscore is not available in shell, and the article used underscores too.

Personaly, i find the underlined ones easier to read:

./roman.sh 32500
CCCI)))CCCI)))CCCI)))CCI))CCI))D

./roman.sh 32500
XXXMMD

And be honest, which one is more 'classic'?

Copyright CI)CI)XIV XII X 
Copyright MMXIV XII X 

But i have to admit, the CI) solution is (alot) easier (than expected) for an algorythm to 'count' with higher numbers.

			*)	# Count it up
				Tone=CI\)
				Tfive=I\)		# Quite irritating to my understanding, but anyway...
				Tten=CCI\)\)
				DEPTH=$[ $len - 3 ]	# 1 = 1000 = initial template
				while [[ $DEPTH -gt 1 ]]
				do	Tone="C$Tone)"
					Tfive="C$Tfive)"
					Tten="C$Tten)"
					DEPTH=$(($DEPTH-1))
				done
				output+="$(roman_val $num $Tone $Tfive $Tten  )"
				;;

Have a nice day! :slight_smile:

His sea...

Ha ha, yeah, but it does look like an overscore and as the terminal has fixed width characters then it is easy to position underscores as overscores where you like... ;o)

@bakunin...
The URL was an AMIGA online magazine of yesteryear and I uploaded lots of kids projects and supporting code articles to it...