RosettaCodeData/Task/UPC/ALGOL-68/upc.alg

228 lines
14 KiB
Plaintext

BEGIN
# number of digits encoded by UPC #
INT upc digits = 12;
# MODE to hold UPC bar code parse results #
MODE UPC = STRUCT( BOOL valid # TRUE if the UPC was valid, #
# FASLE otherwise #
, [ 1 : upc digits ]INT digits
# the digits encoded by the #
# UPC if it was valid #
, BOOL inverted # TRUE if the code was scanned #
# upside down, FALSE if it was #
# right-side up #
, STRING error message # erro rmessage if the string #
# was invalid #
);
# parses the UPC string s and returns a UPC containing the results #
PROC parse upc = ( STRING s )UPC:
BEGIN
# returns TRUE if we are at the end of s, FALSE otherwise #
PROC at end = BOOL: s pos > UPB s;
# counts and skips spaces in s #
PROC skip spaces = INT:
BEGIN
INT spaces := 0;
WHILE IF at end THEN FALSE ELSE s[ s pos ] = " " FI DO
spaces +:= 1;
s pos +:= 1
OD;
spaces
END; # skip spaces #
# skips over the next number of bits characters of s and returns #
# a bit representation ( space = 0, anything else = 1 ) #
PROC get value = ( INT number of bits )BITS:
BEGIN
BITS value := 2r0;
FOR b TO number of bits WHILE NOT at end DO
value := ( value SHL 1 ) OR IF s[ s pos ] = " " THEN 2r0 ELSE 2r1 FI;
s pos +:= 1
OD;
value
END; # get value #
# the representations of the digits #
[]BITS representations = ( 2r 0 0 0 1 1 0 1 # 0 #
, 2r 0 0 1 1 0 0 1 # 1 #
, 2r 0 0 1 0 0 1 1 # 2 #
, 2r 0 1 1 1 1 0 1 # 3 #
, 2r 0 1 0 0 0 1 1 # 4 #
, 2r 0 1 1 0 0 0 1 # 5 #
, 2r 0 1 0 1 1 1 1 # 6 #
, 2r 0 1 1 1 0 1 1 # 7 #
, 2r 0 1 1 0 1 1 1 # 8 #
, 2r 0 0 0 1 0 1 1 # 9 #
);
[]BITS reversed digits = ( 2r 0 1 0 0 1 1 1 # 0 #
, 2r 0 1 1 0 0 1 1 # 1 #
, 2r 0 0 1 1 0 1 1 # 2 #
, 2r 0 1 0 0 0 0 1 # 3 #
, 2r 0 0 1 1 1 0 1 # 4 #
, 2r 0 1 1 1 0 0 1 # 5 #
, 2r 0 0 0 0 1 0 1 # 6 #
, 2r 0 0 1 0 0 0 1 # 7 #
, 2r 0 0 0 1 0 0 1 # 8 #
, 2r 0 0 1 0 1 1 1 # 9 #
);
# number of digits in the left and right sides of the UPC #
INT digits per side = upc digits OVER 2;
UPC result;
FOR d TO upc digits DO ( digits OF result )[ d ] := 0 OD;
inverted OF result := FALSE;
error message OF result := "unknown error";
valid OF result := FALSE;
INT s pos := LWB s;
IF skip spaces < 6 # should be 9 but we are being tolerant #
THEN
# insufficient leading spaces #
error message OF result := "missing leading spaces"
ELIF get value( 3 ) /= 2r101 THEN
# no start #
error message OF result := "missing start sequence"
ELSE
# ok so far - should now have six digits, each encoded in #
# seven bits #
# note we store the digits as 1..10 if the are right-sid up #
# and -1..-10 if they are inverted, so we can distinguish #
# right-side up 0 and inverted 0 #
# the digits are corrected to be 0..9 later #
BOOL valid digits := TRUE;
FOR d TO digits per side WHILE valid digits DO
BITS code = get value( 7 );
BOOL found digit := FALSE;
FOR d pos TO UPB representations WHILE NOT found digit DO
IF code = representations[ d pos ] THEN
# found a "normal" digit #
found digit := TRUE;
( digits OF result )[ d ] := d pos
ELIF code = reversed digits[ d pos ] THEN
# found a reversed digit #
found digit := TRUE;
inverted OF result := TRUE;
( digits OF result )[ d ] := - d pos
FI
OD;
IF NOT found digit THEN
# have an invalid digit #
error message OF result := "invalid digit " + whole( d, 0 );
valid digits := FALSE
FI
OD;
IF NOT valid digits THEN
# had an error #
SKIP
ELIF get value( 5 ) /= 2r01010 THEN
# no middle separator #
error message OF result := "missing middle sequence"
ELSE
# should now have 6 negated digits #
FOR d FROM digits per side + 1 TO upc digits WHILE valid digits DO
BITS code = NOT get value( 7 ) AND 16r7f;
BOOL found digit := FALSE;
FOR d pos TO UPB representations WHILE NOT found digit DO
IF code = representations[ d pos ] THEN
# found a normal negated digit #
found digit := TRUE;
( digits OF result )[ d ] := d pos
ELIF code = reversed digits[ d pos ] THEN
# found reversed negated digit #
found digit := TRUE;
inverted OF result := TRUE;
( digits OF result )[ d ] := - d pos
FI
OD;
IF NOT found digit THEN
# have an invalid digit #
error message OF result := "invalid digit " + whole( d, 0 );
valid digits := FALSE
FI
OD;
IF NOT valid digits THEN
# had an error #
SKIP
ELIF get value( 3 ) /= 2r101 THEN
# no end sequence #
error message OF result := "missing end sequence"
ELIF skip spaces < 6 # should be 9 but we are being #
# tolerant #
THEN
# insufficient trailing spaces #
error message OF result := "insufficient trailing spaces"
ELIF NOT at end THEN
# extraneous stuff after the trailing spaces #
error message OF result := "unexpected trailing bits"
ELSE
# valid so far - if there were reversed digits, #
# check they were all reversed #
# and correct the digits to be in 0..9 #
BOOL all reversed := TRUE;
FOR d TO upc digits DO
IF ( digits OF result )[ d ] < 0 THEN
# reversd digit #
( digits OF result )[ d ] := ABS ( digits OF result )[ d ] - 1
ELSE
# normal digit #
( digits OF result )[ d ] -:= 1;
all reversed := FALSE
FI
OD;
IF inverted OF result AND NOT all reversed
THEN
# had a mixture of inverted and non-inverted #
# digits #
error message OF result := "some reversed digits found"
ELSE
# the code appears valid - check the check sum #
IF inverted OF result THEN
# the code was reversed - reverse the digits #
FOR d TO digits per side DO
INT right digit
= ( digits OF result )[ ( upc digits + 1 ) - d ];
( digits OF result )[ ( upc digits + 1 ) - d ]
:= ( digits OF result )[ d ];
( digits OF result )[ d ] := right digit
OD
FI;
INT check sum := 0;
FOR d FROM 1 BY 2 TO upc digits DO check sum +:= 3 * ( digits OF result )[ d ] OD;
FOR d FROM 2 BY 2 TO upc digits DO check sum +:= ( digits OF result )[ d ] OD;
IF check sum MOD 10 /= 0 THEN
# invalid check digit #
error message OF result := "incorrect check digit"
ELSE
# the UPC code appears valid #
valid OF result := TRUE
FI
FI
FI
FI
FI;
result
END; # parse upc #
# task test cases #
[]STRING tests =
( " # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # "
, " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # "
, " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # "
, " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # "
, " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # "
, " # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # "
, " # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # "
, " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # "
, " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # "
, " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
);
FOR t pos FROM LWB tests TO UPB tests DO
UPC code = parse upc( tests[ t pos ] );
print( ( whole( t pos, -2 ), ":" ) );
IF NOT valid OF code THEN
# invalid UPC code #
print( ( " error: ", error message OF code, newline ) )
ELSE
# valid code #
FOR d TO upc digits - 1 DO print( ( whole( ( digits OF code )[ d ], -2 ) ) ) OD;
print( ( " valid" ) );
IF inverted OF code THEN print( ( " (inverted)" ) ) FI;
print( ( newline ) )
FI
OD
END