\ roman 05.1.24 NAB

needs toolkit
needs core-ext
needs graphics

module roman

decimal  127 array romantable

: set ( n <c> -- )  char  romantable ! ;

\ uppercase symbols return values
\ lowercase returns symbol*10
1 set I        char X set i
10 set X        char C set x
100 set C        char M set c
1000 set M    char ? set m
5 set V        char L set v
50 set L        char D set l
500 set D        char ? set d

: symbol>value ( char -- value )
  >upper romantable @ ;
: symbol10x ( sym -- sym*10 )
  >lower romantable @ ;

: rn ( <digit> <string> -- )
  here set  bl parse place, ;

\ 0..9 returns Roman string
\ note: next line must end after the 0
rn 0
rn 1 I  rn 2 II  rn 3 III  rn 4 IV
rn 5 V  rn 6 VI  rn 7 VII  rn 8 VIII  rn 9 IX

public:

: digit>roman ( c -- addr )
  romantable @ ;

: roman> ( addr u -- u )
\ Decode a Roman number string:
( Malformed strings are accepted )
  0 0 2swap ( n prev addr u )
  bounds ?do ( n prev )
    i c@ symbol>value  ( n prev d )
\ If prev is 1/10th or 1/5th, adjust d:
    over 10 *  2dup = >r
    2/ over =  r> or  ( n prev d flag )
    rot 2* and  -  ( n d-2*prev? )
    dup under+  ( n+d prev )
  loop  drop  ( n ) ;

: roman10x ( addr u -- )
\ Multiply a Roman number by 10:
( String is modified in place )
  bounds ?do
    i c@  symbol10x  i c!
  loop ;

private:

\ max length = 15 chars,
\ 3888 = MMMDCCCLXXXVIII
create romanbuf 16 allot

public:

: >roman ( u1 -- addr u2 )
\ Compute Roman representation:
( u1 must be [0..3999]--not checked )
( Output is in fixed buffer )
  base @ >r  decimal  0 <# #s #>
  r> base !
  romanbuf 0  2swap  bounds ?do
    2dup roman10x
    i c@ digit>roman count append
  loop ;

private:

: bartype ( u -- )
\ Display type with overscore:
  cursor-position
  2swap  tuck  type
  cursor-position rot  if  2 - line
  else  2drop 2drop  then ;

public:

: split-roman ( u -- nnn 1000's )
  dup 0 1000 um/mod ( n r q )
  dup 3 > if
    rot drop
  else  2drop 0  then ;

: romantype ( +n -- )
  split-roman
  >roman bartype
  >roman type ;

end-module
