# ACTIVESTATE TEAPOT-PKG BEGIN TM -*- tcl -*- # -- Tcl Module # @@ Meta Begin # Package math::roman 1.0 # Meta as::build::date 2015-05-25 # Meta as::origin http://sourceforge.net/projects/tcllib # Meta category Tcl Math Library # Meta description Tools for creating and manipulating roman numerals # Meta license BSD # Meta platform tcl # Meta require {Tcl 8.3} # Meta subject {roman numeral} integer conversion # Meta summary math::roman # @@ Meta End # ACTIVESTATE TEAPOT-PKG BEGIN REQUIREMENTS package require Tcl 8.3 # ACTIVESTATE TEAPOT-PKG END REQUIREMENTS # ACTIVESTATE TEAPOT-PKG BEGIN DECLARE package provide math::roman 1.0 # ACTIVESTATE TEAPOT-PKG END DECLARE # ACTIVESTATE TEAPOT-PKG END TM #========================================================================== # Roman Numeral Utility Functions #========================================================================== # Description # # A set of utility routines for handling and manipulating # roman numerals. #------------------------------------------------------------------------- # Copyright/License # # This code was originally harvested from the Tcler's # wiki at http://wiki.tcl.tk/1823 and as such is free # for any use for any purpose. #------------------------------------------------------------------------- # Modification history # # 27 Sep 2005 Kenneth Green # Original version derived from wiki code #------------------------------------------------------------------------- package provide math::roman 1.0 #========================================================================== # Namespace #========================================================================== namespace eval ::math::roman { namespace export tointeger toroman # We dont export 'sort' or 'expr' to prevent collision # with existing commands. These functions are less likely to be # commonly used and have to be accessed as fully-scoped names. # romanvalues - array that maps roman letters to integer values. # variable romanvalues # i2r - list of integer-roman tuples variable i2r {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} # sortkey - list of patterns to supporting sorting of roman numerals variable sortkey {IX VIIII L Y XC YXXXX C Z D {\^} ZM {\^ZZZZ} M _} variable rsortkey {_ M {\^ZZZZ} ZM {\^} D Z C YXXXX XC Y L VIIII IX} # Initialise array variables array set romanvalues {M 1000 D 500 C 100 L 50 X 10 V 5 I 1} } #========================================================================== # Public Functions #========================================================================== #---------------------------------------------------------- # Roman numerals sorted # proc ::math::roman::sort list { variable sortkey variable rsortkey foreach {from to} $sortkey { regsub -all $from $list $to list } set list [lsort $list] foreach {from to} $rsortkey { regsub -all $from $list $to list } return $list } #---------------------------------------------------------- # Roman numerals from integer # proc ::math::roman::toroman {i} { variable i2r set res "" foreach {value roman} $i2r { while {$i>=$value} { append res $roman incr i -$value } } return $res } #---------------------------------------------------------- # Roman numerals parsed into integer: # proc ::math::roman::tointeger {s} { variable romanvalues set last 99999 set res 0 foreach i [split [string toupper $s] ""] { if { [catch {set val $romanvalues($i)}] } { return -code error "roman::tointeger - un-Roman digit $i in $s" } incr res $val if { $val > $last } { incr res [::expr -2*$last] } set last $val } return $res } #---------------------------------------------------------- # Roman numeral arithmetic # proc ::math::roman::expr args { if { [string first \$ $args] >= 0 } { set args [uplevel subst $args] } regsub -all {[^IVXLCDM]} $args { & } args foreach i $args { catch {set i [tointeger $i]} lappend res $i } return [toroman [::expr $res]] } #========================================================== # Developer test code # if { 0 } { puts "Basic int-to-roman-to-int conversion test" for { set i 0 } {$i < 50} {incr i} { set r [::math::roman::toroman $i] set j [::math::roman::tointeger $r] puts [format "%5d %-15s %s" $i $r $j] if { $i != $j } { error "Invalid conversion: $i -> $r -> $j" } } puts "" puts "roman arithmetic test" set x 23 set xr [::math::roman::toroman $x] set y 77 set yr [::math::roman::toroman $y] set xr+yr [::math::roman::expr $xr + $yr] set yr-xr [::math::roman::expr $yr - $xr] set xr*yr [::math::roman::expr $xr * $yr] set yr/xr [::math::roman::expr $yr / $xr] set yr/xr2 [::math::roman::expr {$yr / $xr}] puts "$x + $y\t\t= [expr $x + $y]" puts "$x * $y\t\t= [expr $x * $y]" puts "$y - $x\t\t= [expr $y - $x]" puts "$y / $x\t\t= [expr $y / $x]" puts "$xr + $yr\t= ${xr+yr} = [::math::roman::tointeger ${xr+yr}]" puts "$xr * $yr\t= ${xr*yr} = [::math::roman::tointeger ${xr*yr}]" puts "$yr - $xr\t= ${yr-xr} = [::math::roman::tointeger ${yr-xr}]" puts "$yr / $xr\t= ${yr/xr} = [::math::roman::tointeger ${yr/xr}]" puts "$yr / $xr\t= ${yr/xr2} = [::math::roman::tointeger ${yr/xr2}]" puts "" puts "roman sorting test" set l {X III IV I V} puts "IN : $l" puts "OUT: [::math::roman::sort $l]" }