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.