Pavel D inherited some… we’ll call it “software”… that helps run warehouse operations for a boiler/heating manufacturer. That software was a Visual FoxPro database.
Now, this application needs to read barcodes off of products in the warehouse. Since the laser-scanners can sometimes mis-read those barcodes, the database uses a custom check-sum algorithm.
FUNCTION GetCheckSum
LPARAMETERS lcSerNum
LOCAL lnCalcSum, lnI, lcCheckSum
m.lnCalcSum=0
FOR lnI=1 TO LEN( m.lcSerNum )
DO CASE
CASE SUBSTR(m.lcSerNum, m.lnI,1)= ' '
m.lnCalcSum= m.lnCalcSum+0*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '!'
m.lnCalcSum= m.lnCalcSum+1*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '"'
m.lnCalcSum= m.lnCalcSum+2*m.lnI
CASE SUBSTR(m.lcSerNum, m.lnI,1)= '#'
m.lnCalcSum= m.lnCalcSum+3*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='$'
m.lnCalcSum=m.lnCalcSum+4*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='%'
m.lnCalcSum=m.lnCalcSum+5*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='&'
m.lnCalcSum=m.lnCalcSum+6*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)="'"
m.lnCalcSum=m.lnCalcSum+7*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='('
m.lnCalcSum=m.lnCalcSum+8*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=')'
m.lnCalcSum=m.lnCalcSum+9*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='*'
m.lnCalcSum=m.lnCalcSum+10*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='+'
m.lnCalcSum=m.lnCalcSum+11*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=','
m.lnCalcSum=m.lnCalcSum+12*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='-'
m.lnCalcSum=m.lnCalcSum+13*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='.'
m.lnCalcSum=m.lnCalcSum+14*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='/'
m.lnCalcSum=m.lnCalcSum+15*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='0'
m.lnCalcSum=m.lnCalcSum+16*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='1'
m.lnCalcSum=m.lnCalcSum+17*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='2'
m.lnCalcSum=m.lnCalcSum+18*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='3'
m.lnCalcSum=m.lnCalcSum+19*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='4'
m.lnCalcSum=m.lnCalcSum+20*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='5'
m.lnCalcSum=m.lnCalcSum+21*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='6'
m.lnCalcSum=m.lnCalcSum+22*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='7'
m.lnCalcSum=m.lnCalcSum+23*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='8'
m.lnCalcSum=m.lnCalcSum+24*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='9'
m.lnCalcSum=m.lnCalcSum+25*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=':'
m.lnCalcSum=m.lnCalcSum+26*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=';'
m.lnCalcSum=m.lnCalcSum+27*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='<'
m.lnCalcSum=m.lnCalcSum+28*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='='
m.lnCalcSum=m.lnCalcSum+29*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='>'
m.lnCalcSum=m.lnCalcSum+30*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='?'
m.lnCalcSum=m.lnCalcSum+31*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='@'
m.lnCalcSum=m.lnCalcSum+32*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='A'
m.lnCalcSum=m.lnCalcSum+33*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='B'
m.lnCalcSum=m.lnCalcSum+34*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='C'
m.lnCalcSum=m.lnCalcSum+35*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='D'
m.lnCalcSum=m.lnCalcSum+36*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='E'
m.lnCalcSum=m.lnCalcSum+37*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='F'
m.lnCalcSum=m.lnCalcSum+38*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='G'
m.lnCalcSum=m.lnCalcSum+39*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='H'
m.lnCalcSum=m.lnCalcSum+40*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='I'
m.lnCalcSum=m.lnCalcSum+41*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='J'
m.lnCalcSum=m.lnCalcSum+42*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='K'
m.lnCalcSum=m.lnCalcSum+43*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='L'
m.lnCalcSum=m.lnCalcSum+44*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='M'
m.lnCalcSum=m.lnCalcSum+45*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='N'
m.lnCalcSum=m.lnCalcSum+46*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='O'
m.lnCalcSum=m.lnCalcSum+47*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='P'
m.lnCalcSum=m.lnCalcSum+48*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Q'
m.lnCalcSum=m.lnCalcSum+49*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='R'
m.lnCalcSum=m.lnCalcSum+50*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='S'
m.lnCalcSum=m.lnCalcSum+51*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='T'
m.lnCalcSum=m.lnCalcSum+52*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='U'
m.lnCalcSum=m.lnCalcSum+53*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='V'
m.lnCalcSum=m.lnCalcSum+54*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='W'
m.lnCalcSum=m.lnCalcSum+55*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='X'
m.lnCalcSum=m.lnCalcSum+56*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Y'
m.lnCalcSum=m.lnCalcSum+57*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='Z'
m.lnCalcSum=m.lnCalcSum+58*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='['
m.lnCalcSum=m.lnCalcSum+59*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='\'
m.lnCalcSum=m.lnCalcSum+60*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)=']'
m.lnCalcSum=m.lnCalcSum+61*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='^'
m.lnCalcSum=m.lnCalcSum+62*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='_'
m.lnCalcSum=m.lnCalcSum+63*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='`'
m.lnCalcSum=m.lnCalcSum+64*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='a'
m.lnCalcSum=m.lnCalcSum+65*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='b'
m.lnCalcSum=m.lnCalcSum+66*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='c'
m.lnCalcSum=m.lnCalcSum+67*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='d'
m.lnCalcSum=m.lnCalcSum+68*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='e'
m.lnCalcSum=m.lnCalcSum+69*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='f'
m.lnCalcSum=m.lnCalcSum+70*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='g'
m.lnCalcSum=m.lnCalcSum+71*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='h'
m.lnCalcSum=m.lnCalcSum+72*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='i'
m.lnCalcSum=m.lnCalcSum+73*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='j'
m.lnCalcSum=m.lnCalcSum+74*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='k'
m.lnCalcSum=m.lnCalcSum+75*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='l'
m.lnCalcSum=m.lnCalcSum+76*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='m'
m.lnCalcSum=m.lnCalcSum+77*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='n'
m.lnCalcSum=m.lnCalcSum+78*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='o'
m.lnCalcSum=m.lnCalcSum+79*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='p'
m.lnCalcSum=m.lnCalcSum+80*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='q'
m.lnCalcSum=m.lnCalcSum+81*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='r'
m.lnCalcSum=m.lnCalcSum+82*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='s'
m.lnCalcSum=m.lnCalcSum+83*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='t'
m.lnCalcSum=m.lnCalcSum+84*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='u'
m.lnCalcSum=m.lnCalcSum+85*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='v'
m.lnCalcSum=m.lnCalcSum+86*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='w'
m.lnCalcSum=m.lnCalcSum+87*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='x'
m.lnCalcSum=m.lnCalcSum+88*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='y'
m.lnCalcSum=m.lnCalcSum+89*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='z'
m.lnCalcSum=m.lnCalcSum+90*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='{'
m.lnCalcSum=m.lnCalcSum+91*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='|'
m.lnCalcSum=m.lnCalcSum+92*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='}'
m.lnCalcSum=m.lnCalcSum+93*m.lnI
CASE SUBSTR(m.lcSerNum,m.lnI,1)='~'
m.lnCalcSum=m.lnCalcSum+94*m.lnI
ENDCASE
ENDFOR
m.lcCheckSum = RIGHT(STR(m.lnCalcSum,7,0),1)
RETURN m.lcCheckSum
ENDFUNC
Now, the obvious issue here is that the entire “CASE ” statement could be replaced with a call to “ASC”, which returns the ASCII code for a given character , but as Pavel notes, “the value of a character at position which equals a multiple of 10 is ignored”.
Pavel was tasked with reimplementing this in Python, and after a little thought, recreated the function in a much more concise fashion:
def validate(sn):
checksum = 0
for i, x in enumerate(sn[:-1]):
checksum += (i + 1) * (ord(x) - 32)
return str(checksum % 10) == sn[-1]