Richard Suchenwirth 1999-11-11 - Member railroads of the UIC (Union internationale des chemins de fer), mostly in Europe, identify goods and passenger cars by 12-digit numbers which indicate category, exchange status, owner, type etc. Some railroads also use 7- or 8-digit numbers for locomotives. In both types, the final digit (separated by a dash) is a check digit (see Check digits) that allows plausibility tests:
The cross-sum of the 12 or 7 digits multiplied alternating by 1 or 2 (starting with 1 at the rightmost digit) must be a multiple of 10, e.g. for validating the number 21 80 155 9 084-5:
2 1 8 0 1 5 5 9 0 8 4 5 * 2 1 2 1 2 1 2 1 2 1 2 1 ---------------------------------------------------- 4 1 16 0 2 5 10 9 0 8 8 5 4 +1+1+6 +0 +2 +5+1+0 +9 +0 +8 +8 +5 = 50, o.k.
The Tcl code for this validation removes all non-digits, so can be called with the complete car inscriptions like
21 RIV 80 DB 155 9 084-5 proc uic:valid {s} { regsub -all {[^0-9]} $s "" no set length [string length $no] if {$length!=12 && $length!=7 && $length!=8} { error "bad number of UIC digits, must be 7, 8, or 12" } regexp {([0-9]+)([0-9]$)} $no -> stem check expr [uic:checkdigit $stem] == $check }
The calculation of the check digit is factored out:
proc uic:checkdigit {no} { set pos [string length $no] set t "" foreach i [split $no ""] { incr pos -1 append t [expr $i*(2-$pos%2)] } expr (10-[cross_sum $t] % 10) % 10 }
Notice the shimmering, but beautiful way to compute the cross sum ;-)
proc cross_sum {s} {expr [join [split $s ""] +]}
Without much ado, the argument goes from string to list to string to int. This is another reason why I love Tcl - and don't care much for Python or Perl.