UPC: Difference between revisions

107,843 bytes added ,  3 months ago
m
(→‎{{header|REXX}}: added the REXX computer programming language for this task.)
m (→‎{{header|Wren}}: Minor tidy)
 
(30 intermediate revisions by 18 users not shown)
Line 1:
{{draft task}}
 
Task: Convert upc barcodes to decimal.
;Goal:
Convert UPC bar codes to decimal.
 
 
Specifically:
 
The [[wp:Universal_Product_Code|UPC]] standard is actually a collection of standards -- physical standards, data format standards, product reference standards... Here, we focus on some of the data format standards, with an imaginary physical+electrical implementation which converts physical UPC barcodes to ascii, with spaces and # characters representing the presence or absence of ink.
 
Here,   in this task,   we will focus on some of the data format standards,   with an imaginary physical+electrical implementation which converts physical UPC bar codes to ASCII   (with spaces and   '''#'''   characters representing the presence or absence of ink).
Here, we have a representation of 10 different UPC-A bar codes read by our imaginary bar code reader:
 
 
;Sample input:
Below, we have a representation of ten different UPC-A bar codes read by our imaginary bar code reader:
<pre>
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
Line 20 ⟶ 26:
# # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
</pre>
Some of these were entered upside down, &nbsp; and one entry has a timing error.
 
Some of these were input upside down, and one has a timing error.
 
;Task:
The task is to implement code to find the corresponding decimal representation of each, rejecting the error. Extra credit for handling the rows entered upside down (the other option is to reject them).
Implement code to find the corresponding decimal representation of each, rejecting the error.
 
Extra credit for handling the rows entered upside down &nbsp; (the other option is to reject them).
Notes:
 
 
;Notes:
Each digit is represented by 7 bits:
<pre>
0: 0 0 0 1 1 0 1
1: 0 0 1 1 0 0 1
2: 0 0 1 0 0 1 1
3: 0 1 1 1 1 0 1
4: 0 1 0 0 0 1 1
5: 0 1 1 0 0 0 1
6: 0 1 0 1 1 1 1
7: 0 1 1 1 0 1 1
8: 0 1 1 0 1 1 1
9: 0 0 0 1 0 1 1
</pre>
On the left hand side of the bar code a space represents a '''0''' and a '''#''' represents a '''1'''.
<br>On the right hand side of the bar code, a '''#''' represents a '''0''' and a space represents a '''1'''
<br>Alternatively (for the above): &nbsp; spaces always represent zeros and '''#''' characters always represent ones, but the representation is logically negated -- '''1'''s and '''0'''s are flipped -- on the right hand side of the bar code.
 
 
 
;The UPC-A bar code structure:
::* &nbsp; It begins with at least 9 spaces &nbsp; (which our imaginary bar code reader unfortunately doesn't always reproduce properly),
::* &nbsp; then has a &nbsp; &nbsp; ''' # # ''' &nbsp; &nbsp; sequence marking the start of the sequence,
::* &nbsp; then has the six "left hand" digits,
::* &nbsp; then has a &nbsp; ''' # # ''' &nbsp; sequence in the middle,
::* &nbsp; then has the six "right hand digits",
::* &nbsp; then has another &nbsp; ''' # # ''' &nbsp; (end sequence), &nbsp; and finally,
::* &nbsp; then ends with nine trailing spaces &nbsp; (which might be eaten by wiki edits, and in any event, were not quite captured correctly by our imaginary bar code reader).
 
 
Finally, the last digit is a checksum digit which may be used to help detect errors.
 
 
;Verification:
Multiply each digit in the represented 12 digit sequence by the corresponding number in &nbsp; (3,1,3,1,3,1,3,1,3,1,3,1) &nbsp; and add the products.
 
The sum (mod 10) must be '''0''' &nbsp; (must have a zero as its last digit) &nbsp; if the UPC number has been read correctly.
<br><br>
 
=={{header|11l}}==
{{trans|Kotlin}}
 
<syntaxhighlight lang="11l">V LEFT_DIGITS = [
‘ ## #’ = 0,
‘ ## #’ = 1,
‘ # ##’ = 2,
‘ #### #’ = 3,
‘ # ##’ = 4,
‘ ## #’ = 5,
‘ # ####’ = 6,
‘ ### ##’ = 7,
‘ ## ###’ = 8,
‘ # ##’ = 9
]
 
V RIGHT_DIGITS = Dict(LEFT_DIGITS.items(), (k, v) -> (k.replace(‘ ’, ‘s’).replace(‘#’, ‘ ’).replace(‘s’, ‘#’), v))
 
V END_SENTINEL = ‘# #’
V MID_SENTINEL = ‘ # # ’
 
F decodeUPC(input)
F decode(candidate)
V pos = 0
V part = candidate[pos .+ :END_SENTINEL.len]
I part == :END_SENTINEL
pos += :END_SENTINEL.len
E
R (0B, [Int]())
 
[Int] output
L 6
part = candidate[pos .+ 7]
pos += 7
 
I part C :LEFT_DIGITS
output [+]= :LEFT_DIGITS[part]
E
R (0B, output)
 
part = candidate[pos .+ :MID_SENTINEL.len]
I part == :MID_SENTINEL
pos += :MID_SENTINEL.len
E
R (0B, output)
 
L 6
part = candidate[pos .+ 7]
pos += 7
 
I part C :RIGHT_DIGITS
output [+]= :RIGHT_DIGITS[part]
E
R (0B, output)
 
part = candidate[pos .+ :END_SENTINEL.len]
I part == :END_SENTINEL
pos += :END_SENTINEL.len
E
R (0B, output)
 
V sum = 0
L(v) output
I L.index % 2 == 0
sum += 3 * v
E
sum += v
R (sum % 10 == 0, output)
 
V candidate = input.trim(‘ ’)
V out = decode(candidate)
I out[0]
print(out[1])
E
out = decode(reversed(candidate))
I out[0]
print(out[1]‘ Upside down’)
E
I out[1].len == 12
print(‘Invalid checksum’)
E
print(‘Invalid digit(s)’)
 
V barcodes = [
‘ # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ’,
‘ # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ’,
‘ # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ’,
‘ # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ’,
‘ # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ’,
‘ # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ’,
‘ # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ’,
‘ # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ’,
‘ # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ’,
‘ # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ’
]
 
L(barcode) barcodes
decodeUPC(barcode)</syntaxhighlight>
 
{{out}}
<pre>
[9, 2, 0:4, 07, 07, 03, 12, 7, 1, 0, 1, 9]
[4, 0, 1:3, 09, 04, 14, 4, 4, 1, 0, 5, 0 1]
[8, 3, 2:4, 09, 09, 19, 06, 7, 6, 7, 0, 6] 1Upside 1down
[9, 3:, 09, 18, 2, 5, 1, 5, 8, 8, 1, 1] 0Upside 1down
Invalid digit(s)
4: 0 1 0 0 0 1 1
[3, 1, 5:6, 03, 1, 13, 07, 01, 08, 7, 1, 7] Upside down
[2, 1, 6:4, 05, 17, 05, 18, 17, 15, 16, 0, 8]
[8, 1, 8, 7:, 07, 18, 18, 4, 1, 08, 1, 13] Upside down
[7, 0, 8:6, 04, 16, 16, 07, 14, 13, 10, 3, 0]
[6, 5, 9:3, 04, 08, 03, 15, 4, 0, 4, 13, 15]
</pre>
 
=={{header|Action!}}==
On the left hand side of the bar code a space represents a 0 and a # represents a 1. On the right hand side of the bar code, a # represents a 0 and a space represents a 1 (alternatively: spaces always represent zeros and # characters always represent ones, but the representation is logically negated -- 1s and 0s are flipped -- on the right hand side of the bar code).
<syntaxhighlight lang="action!">DEFINE PTR="CARD"
DEFINE RESOK="255"
DEFINE RESUPSIDEDOWN="254"
DEFINE RESINVALID="253"
DEFINE DIGITCOUNT="12"
DEFINE DIGITLEN="7"
 
PTR ARRAY ldigits(10),rdigits(10)
The UPC-A barcode structure begins with at least 9 spaces (which our imaginary bar code reader unfortunately doesn't always reproduce properly), then has a '# #' sequence marking the start of the sequence, then has the six "left hand" digits, then has a ' # # ' sequence in the middle, then has the six "right hand digits" and finally ends with another '# #' end sequence and nine trailing spaces (which might be eaten by wiki edits and in any event were not quite captured correctly by our imaginary bar code reader).
CHAR ARRAY marker="# #",midmarker=" # # "
 
PROC Init()
Finally, the last digit is a checksum digit which may be used to help detect errors. Multiply each digit in the represented 12 digit sequence by the corresponding number in (3,1,3,1,3,1,3,1,3,1,3,1) and add products, and the sum (mod 10) must be 0 (must have a zero as its last digit) if the number has been read correctly.
ldigits(0)=" ## #" ldigits(1)=" ## #"
ldigits(2)=" # ##" ldigits(3)=" #### #"
ldigits(4)=" # ##" ldigits(5)=" ## #"
ldigits(6)=" # ####" ldigits(7)=" ### ##"
ldigits(8)=" ## ###" ldigits(9)=" # ##"
rdigits(0)="### # " rdigits(1)="## ## "
rdigits(2)="## ## " rdigits(3)="# # "
rdigits(4)="# ### " rdigits(5)="# ### "
rdigits(6)="# # " rdigits(7)="# # "
rdigits(8)="# # " rdigits(9)="### # "
RETURN
 
BYTE FUNC DecodeMarker(CHAR ARRAY s BYTE POINTER pos CHAR ARRAY marker)
CHAR ARRAY tmp(6)
BYTE x
 
x=pos^+marker(0)
IF x>s(0) THEN
RETURN (RESINVALID)
ELSE
SCopyS(tmp,s,pos^,pos^+marker(0)-1)
pos^==+marker(0)
IF SCompare(tmp,marker)#0 THEN
RETURN (RESINVALID)
FI
FI
RETURN (RESOK)
 
BYTE FUNC DecodeDigit(CHAR ARRAY s BYTE POINTER pos PTR ARRAY digits)
CHAR ARRAY tmp(DIGITLEN+1)
BYTE i,x
 
x=pos^+DIGITLEN
IF x>s(0) THEN
RETURN (RESINVALID)
ELSE
SCopyS(tmp,s,pos^,pos^+DIGITLEN-1)
pos^==+DIGITLEN
FOR i=0 TO 9
DO
IF SCompare(tmp,digits(i))=0 THEN
RETURN (i)
FI
OD
FI
RETURN (RESINVALID)
 
BYTE FUNC Validation(BYTE ARRAY code)
BYTE ARRAY mult=[3 1 3 1 3 1 3 1 3 1 3 1]
BYTE i
INT sum
 
sum=0
FOR i=0 TO DIGITCOUNT-1
DO
sum==+code(i)*mult(i)
OD
IF sum MOD 10=0 THEN
RETURN (RESOK)
FI
RETURN (RESINVALID)
 
BYTE FUNC DecodeInternal(CHAR ARRAY s BYTE ARRAY code)
BYTE res,pos,i
 
pos=1
WHILE pos<=s(0) AND s(pos)=32
DO pos==+1 OD
 
res=DecodeMarker(s,@pos,marker)
IF res=RESINVALID THEN RETURN (res) FI
 
FOR i=0 TO 5
DO
res=DecodeDigit(s,@pos,ldigits)
IF res=RESINVALID THEN RETURN (res) FI
code(i)=res
OD
res=DecodeMarker(s,@pos,midmarker)
IF res=RESINVALID THEN RETURN (res) FI
 
FOR i=6 TO 11
DO
res=DecodeDigit(s,@pos,rdigits)
IF res=RESINVALID THEN RETURN (res) FI
code(i)=res
OD
 
res=DecodeMarker(s,@pos,marker)
IF res=RESINVALID THEN RETURN (res) FI
res=Validation(code)
RETURN (res)
 
PROC Reverse(CHAR ARRAY src,dst)
BYTE i,j
 
i=1 j=src(0) dst(0)=j
WHILE j>0
DO
dst(j)=src(i)
i==+1 j==-1
OD
RETURN
 
BYTE FUNC Decode(CHAR ARRAY s BYTE ARRAY code)
CHAR ARRAY tmp(256)
BYTE res
 
res=DecodeInternal(s,code)
IF res=RESOK THEN RETURN (res) FI
Reverse(s,tmp)
res=DecodeInternal(tmp,code)
IF res=RESINVALID THEN RETURN (res) FI
RETURN (RESUPSIDEDOWN)
 
PROC Test(BYTE id CHAR ARRAY s)
BYTE ARRAY code(DIGITCOUNT)
BYTE res,i
 
res=Decode(s,code)
IF id<10 THEN Put(32) FI
PrintF("%B: ",id)
IF res=RESINVALID THEN
PrintE("invalid")
ELSE
FOR i=0 TO DIGITCOUNT-1
DO
PrintB(code(i))
OD
IF res=RESUPSIDEDOWN THEN
PrintE(" valid (upside down)")
ELSE
PrintE(" valid")
FI
FI
RETURN
 
PROC Main()
PTR ARRAY codes(10)
BYTE i
Init()
codes(0)=" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # "
codes(1)=" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # "
codes(2)=" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # "
codes(3)=" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # "
codes(4)=" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # "
codes(5)=" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # "
codes(6)=" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # "
codes(7)=" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # "
codes(8)=" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # "
codes(9)=" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
FOR i=0 TO 9
DO
Test(i+1,codes(i))
OD
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/UPC.png Screenshot from Atari 8-bit computer]
<pre>
1: 924773271019 valid
2: 403944441050 valid
3: 834999676706 valid (upside down)
4: 939825158811 valid (upside down)
5: invalid
6: 316313718717 valid (upside down)
7: 214575875608 valid
8: 818778841813 valid (upside down)
9: 706466743030 valid
10: 653483540435 valid
</pre>
 
=={{header|ALGOL 68}}==
<syntaxhighlight lang="algol68">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</syntaxhighlight>
{{out}}
<pre>
1: 9 2 4 7 7 3 2 7 1 0 1 valid
2: 4 0 3 9 4 4 4 4 1 0 5 valid
3: 8 3 4 9 9 9 6 7 6 7 0 valid (inverted)
4: 9 3 9 8 2 5 1 5 8 8 1 valid (inverted)
5: error: invalid digit 12
6: 3 1 6 3 1 3 7 1 8 7 1 valid (inverted)
7: 2 1 4 5 7 5 8 7 5 6 0 valid
8: 8 1 8 7 7 8 8 4 1 8 1 valid (inverted)
9: 7 0 6 4 6 6 7 4 3 0 3 valid
10: 6 5 3 4 8 3 5 4 0 4 3 valid
</pre>
 
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">UPC2Dec(code){
lBits :={" ## #":0," ## #":1," # ##":2," #### #":3," # ##":4," ## #":5," # ####":6," ### ##":7," ## ###":8," # ##":9}
xlBits:={"# ## ":0,"# ## ":1,"## # ":2,"# #### ":3,"## # ":4,"# ## ":5,"#### # ":6,"## ### ":7,"### ## ":8,"## # ":9}
rBits :={"### # ":0,"## ## ":1,"## ## ":2,"# # ":3,"# ### ":4,"# ### ":5,"# # ":6,"# # ":7,"# # ":8,"### # ":9}
xrBits:={" # ###":0," ## ##":1," ## ##":2," # #":3," ### #":4," ### #":5," # #":6," # #":7," # #":8," # ###":9}
UPC := "", CD := 0, code := Trim(code, " ")
S := SubStr(code, 1, 3), code := SubStr(code, 4) ; start or "upside down" end sequence
loop 6
C := SubStr(code, 1, 7), code := SubStr(code, 8) ; six left hand or "upside down" right hand digits
, UPC := lBits[C] <> "" ? UPC . lBits[C] : xrBits[C] . UPC
M := SubStr(code, 1, 5), code := SubStr(code, 6) ; middle sequence
loop 6
C := SubStr(code, 1, 7), code := SubStr(code, 8) ; six right hand or "upside down" left hand digits
, UPC := rBits[C] <> "" ? UPC . rBits[C] : xlBits[C] . UPC
E := SubStr(code, 1, 3), code := SubStr(code, 4) ; end or "upside down" start sequence
for k, v in StrSplit(UPC)
CD += Mod(A_Index, 2) ? v*3 : v ; Check Digit
if (S <> "# #") || (M <> " # # ") || (E <> "# #") || Mod(CD, 10)
return "Invalid!"
return UPC
}
</syntaxhighlight>
Examples:<syntaxhighlight lang="autohotkey">data =
(
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
# # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # #
# # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # #
# # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # #
# # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # #
# # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # #
# # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # #
# # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # #
# # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # #
# # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
)
for i, code in StrSplit(data, "`n", "`r")
output .= i ":`t" UPC2Dec(code) "`n"
MsgBox, 262144, ,% output
return</syntaxhighlight>
{{out}}
<pre>
1: 924773271019
2: 403944441050
3: 834999676706
4: 939825158811
5: Invalid!
6: 316313718717
7: 214575875608
8: 818778841813
9: 706466743030
10: 653483540435
</pre>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
<lang AWK>
# syntax: GAWK -f UPC.AWK
BEGIN {
Line 119 ⟶ 740:
return(sum)
}
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 135 ⟶ 756:
11: invalid format # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
</pre>
 
=={{header|C}}==
<syntaxhighlight lang="c">#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef char const *const string;
 
bool consume_sentinal(bool middle, string s, size_t *pos) {
if (middle) {
if (s[*pos] == ' ' && s[*pos + 1] == '#' && s[*pos + 2] == ' ' && s[*pos + 3] == '#' && s[*pos + 4] == ' ') {
*pos += 5;
return true;
}
} else {
if (s[*pos] == '#' && s[*pos + 1] == ' ' && s[*pos + 2] == '#') {
*pos += 3;
return true;
}
}
return false;
}
 
int consume_digit(bool right, string s, size_t *pos) {
const char zero = right ? '#' : ' ';
const char one = right ? ' ' : '#';
size_t i = *pos;
int result = -1;
 
if (s[i] == zero) {
if (s[i + 1] == zero) {
if (s[i + 2] == zero) {
if (s[i + 3] == one) {
if (s[i + 4] == zero) {
if (s[i + 5] == one && s[i + 6] == one) {
result = 9;
}
} else if (s[i + 4] == one) {
if (s[i + 5] == zero && s[i + 6] == one) {
result = 0;
}
}
}
} else if (s[i + 2] == one) {
if (s[i + 3] == zero) {
if (s[i + 4] == zero && s[i + 5] == one && s[i + 6] == one) {
result = 2;
}
} else if (s[i + 3] == one) {
if (s[i + 4] == zero && s[i + 5] == zero && s[i + 6] == one) {
result = 1;
}
}
}
} else if (s[i + 1] == one) {
if (s[i + 2] == zero) {
if (s[i + 3] == zero) {
if (s[i + 4] == zero && s[i + 5] == one && s[i + 6] == one) {
result = 4;
}
} else if (s[i + 3] == one) {
if (s[i + 4] == one && s[i + 5] == one && s[i + 6] == one) {
result = 6;
}
}
} else if (s[i + 2] == one) {
if (s[i + 3] == zero) {
if (s[i + 4] == zero) {
if (s[i + 5] == zero && s[i + 6] == one) {
result = 5;
}
} else if (s[i + 4] == one) {
if (s[i + 5] == one && s[i + 6] == one) {
result = 8;
}
}
} else if (s[i + 3] == one) {
if (s[i + 4] == zero) {
if (s[i + 5] == one && s[i + 6] == one) {
result = 7;
}
} else if (s[i + 4] == one) {
if (s[i + 5] == zero && s[i + 6] == one) {
result = 3;
}
}
}
}
}
}
 
if (result >= 0) {
*pos += 7;
}
return result;
}
 
bool decode_upc(string src, char *buffer) {
const int one = 1;
const int three = 3;
 
size_t pos = 0;
int sum = 0;
int digit;
 
//1) 9 spaces (unreliable)
while (src[pos] != '#') {
if (src[pos] == 0) {
return false;
}
pos++;
}
 
//2) Start "# #"
if (!consume_sentinal(false, src, &pos)) {
return false;
}
 
//3) 6 left-hand digits (space is zero and hash is one)
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(false, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
//4) Middle "# #"
if (!consume_sentinal(true, src, &pos)) {
return false;
}
 
//5) 6 right-hand digits (hash is zero and space is one)
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += three * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
digit = consume_digit(true, src, &pos);
if (digit < 0) {
return false;
}
sum += one * digit;
*buffer++ = digit + '0';
*buffer++ = ' ';
 
//6) Final "# #"
if (!consume_sentinal(false, src, &pos)) {
return false;
}
 
//7) 9 spaces (unreliable)
// skip
 
//8) the dot product of the number and (3, 1)+ sequence mod 10 must be zero
return sum % 10 == 0;
}
 
void test(string src) {
char buffer[24];
 
if (decode_upc(src, buffer)) {
buffer[22] = 0;
printf("%sValid\n", buffer);
} else {
size_t len = strlen(src);
char *rev = malloc(len + 1);
size_t i;
 
if (rev == NULL) {
exit(1);
}
 
for (i = 0; i < len; i++) {
rev[i] = src[len - i - 1];
}
 
#pragma warning(push)
#pragma warning(disable : 6386)
// if len + 1 bytes are allocated, and len bytes are writable, there is no buffer overrun
rev[len] = 0;
#pragma warning(pop)
 
if (decode_upc(rev, buffer)) {
buffer[22] = 0;
printf("%sValid (upside down)\n", buffer);
} else {
printf("Invalid digit(s)\n");
}
 
free(rev);
}
}
 
int main() {
int num = 0;
 
printf("%2d: ", ++num);
test(" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ");
 
printf("%2d: ", ++num);
test(" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ");
 
printf("%2d: ", ++num);
test(" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ");
 
printf("%2d: ", ++num);
test(" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ");
 
printf("%2d: ", ++num);
test(" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ");
 
printf("%2d: ", ++num);
test(" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ");
 
printf("%2d: ", ++num);
test(" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ");
 
printf("%2d: ", ++num);
test(" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ");
 
printf("%2d: ", ++num);
test(" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ");
 
printf("%2d: ", ++num);
test(" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ");
 
return 0;
}</syntaxhighlight>
{{out}}
<pre> 1: 9 2 4 7 7 3 2 7 1 0 1 Valid
2: 4 0 3 9 4 4 4 4 1 0 5 Valid
3: 8 3 4 9 9 9 6 7 6 7 0 Valid (upside down)
4: 9 3 9 8 2 5 1 5 8 8 1 Valid (upside down)
5: Invalid digit(s)
6: 3 1 6 3 1 3 7 1 8 7 1 Valid (upside down)
7: 2 1 4 5 7 5 8 7 5 6 0 Valid
8: 8 1 8 7 7 8 8 4 1 8 1 Valid (upside down)
9: 7 0 6 4 6 6 7 4 3 0 3 Valid
10: 6 5 3 4 8 3 5 4 0 4 3 Valid</pre>
 
=={{header|C++}}==
{{trans|Kotlin}}
<syntaxhighlight lang="cpp">#include <iostream>
#include <locale>
#include <map>
#include <vector>
 
std::string trim(const std::string &str) {
auto s = str;
 
//rtrim
auto it1 = std::find_if(s.rbegin(), s.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale::classic()); });
s.erase(it1.base(), s.end());
 
//ltrim
auto it2 = std::find_if(s.begin(), s.end(), [](char ch) { return !std::isspace<char>(ch, std::locale::classic()); });
s.erase(s.begin(), it2);
 
return s;
}
 
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
auto it = v.cbegin();
auto end = v.cend();
 
os << '[';
if (it != end) {
os << *it;
it = std::next(it);
}
while (it != end) {
os << ", " << *it;
it = std::next(it);
}
return os << ']';
}
 
const std::map<std::string, int> LEFT_DIGITS = {
{" ## #", 0},
{" ## #", 1},
{" # ##", 2},
{" #### #", 3},
{" # ##", 4},
{" ## #", 5},
{" # ####", 6},
{" ### ##", 7},
{" ## ###", 8},
{" # ##", 9}
};
 
const std::map<std::string, int> RIGHT_DIGITS = {
{"### # ", 0},
{"## ## ", 1},
{"## ## ", 2},
{"# # ", 3},
{"# ### ", 4},
{"# ### ", 5},
{"# # ", 6},
{"# # ", 7},
{"# # ", 8},
{"### # ", 9}
};
 
const std::string END_SENTINEL = "# #";
const std::string MID_SENTINEL = " # # ";
 
void decodeUPC(const std::string &input) {
auto decode = [](const std::string &candidate) {
using OT = std::vector<int>;
OT output;
size_t pos = 0;
 
auto part = candidate.substr(pos, END_SENTINEL.length());
if (part == END_SENTINEL) {
pos += END_SENTINEL.length();
} else {
return std::make_pair(false, OT{});
}
 
for (size_t i = 0; i < 6; i++) {
part = candidate.substr(pos, 7);
pos += 7;
 
auto e = LEFT_DIGITS.find(part);
if (e != LEFT_DIGITS.end()) {
output.push_back(e->second);
} else {
return std::make_pair(false, output);
}
}
 
part = candidate.substr(pos, MID_SENTINEL.length());
if (part == MID_SENTINEL) {
pos += MID_SENTINEL.length();
} else {
return std::make_pair(false, OT{});
}
 
for (size_t i = 0; i < 6; i++) {
part = candidate.substr(pos, 7);
pos += 7;
 
auto e = RIGHT_DIGITS.find(part);
if (e != RIGHT_DIGITS.end()) {
output.push_back(e->second);
} else {
return std::make_pair(false, output);
}
}
 
part = candidate.substr(pos, END_SENTINEL.length());
if (part == END_SENTINEL) {
pos += END_SENTINEL.length();
} else {
return std::make_pair(false, OT{});
}
 
int sum = 0;
for (size_t i = 0; i < output.size(); i++) {
if (i % 2 == 0) {
sum += 3 * output[i];
} else {
sum += output[i];
}
}
return std::make_pair(sum % 10 == 0, output);
};
 
auto candidate = trim(input);
 
auto out = decode(candidate);
if (out.first) {
std::cout << out.second << '\n';
} else {
std::reverse(candidate.begin(), candidate.end());
out = decode(candidate);
if (out.first) {
std::cout << out.second << " Upside down\n";
} else if (out.second.size()) {
std::cout << "Invalid checksum\n";
} else {
std::cout << "Invalid digit(s)\n";
}
}
}
 
int main() {
std::vector<std::string> barcodes = {
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
};
for (auto &barcode : barcodes) {
decodeUPC(barcode);
}
return 0;
}</syntaxhighlight>
{{out}}
<pre>[9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9]
[4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0]
[8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside down
[9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside down
Invalid digit(s)
[3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside down
[2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8]
[8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside down
[7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0]
[6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5]</pre>
 
=={{header|D}}==
{{trans|C++}}
<syntaxhighlight lang="d">import std.algorithm : countUntil, each, map;
import std.array : array;
import std.conv : to;
import std.range : empty, retro;
import std.stdio : writeln;
import std.string : strip;
import std.typecons : tuple;
 
immutable LEFT_DIGITS = [
" ## #",
" ## #",
" # ##",
" #### #",
" # ##",
" ## #",
" # ####",
" ### ##",
" ## ###",
" # ##"
];
immutable RIGHT_DIGITS = LEFT_DIGITS.map!`a.map!"a == '#' ? ' ' : '#'".array`.array;
 
immutable END_SENTINEL = "# #";
immutable MID_SENTINEL = " # # ";
 
void decodeUPC(string input) {
auto decode(string candidate) {
int[] output;
size_t pos = 0;
 
auto next = pos + END_SENTINEL.length;
auto part = candidate[pos .. next];
if (part == END_SENTINEL) {
pos = next;
} else {
return tuple(false, cast(int[])[]);
}
 
foreach (_; 0..6) {
next = pos + 7;
part = candidate[pos .. next];
auto i = countUntil(LEFT_DIGITS, part);
if (i >= 0) {
output ~= i;
pos = next;
} else {
return tuple(false, cast(int[])[]);
}
}
 
next = pos + MID_SENTINEL.length;
part = candidate[pos .. next];
if (part == MID_SENTINEL) {
pos = next;
} else {
return tuple(false, cast(int[])[]);
}
 
foreach (_; 0..6) {
next = pos + 7;
part = candidate[pos .. next];
auto i = countUntil(RIGHT_DIGITS, part);
if (i >= 0) {
output ~= i;
pos = next;
} else {
return tuple(false, cast(int[])[]);
}
}
 
next = pos + END_SENTINEL.length;
part = candidate[pos .. next];
if (part == END_SENTINEL) {
pos = next;
} else {
return tuple(false, cast(int[])[]);
}
 
int sum = 0;
foreach (i,v; output) {
if (i % 2 == 0) {
sum += 3 * v;
} else {
sum += v;
}
}
return tuple(sum % 10 == 0, output);
}
 
auto candidate = input.strip;
auto output = decode(candidate);
if (output[0]) {
writeln(output[1]);
} else {
output = decode(candidate.retro.array.to!string);
if (output[0]) {
writeln(output[1], " Upside down");
} else if (output[1].empty) {
writeln("Invalid digit(s)");
} else {
writeln("Invalid checksum", output);
}
}
}
 
void main() {
auto barcodes = [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
];
barcodes.each!decodeUPC;
}</syntaxhighlight>
{{out}}
<pre>[9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9]
[4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0]
[8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside down
[9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside down
Invalid digit(s)
[3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside down
[2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8]
[8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside down
[7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0]
[6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5]</pre>
 
=={{header|Factor}}==
{{works with|Factor|0.99 2019-10-06}}
<langsyntaxhighlight lang="factor">USING: combinators combinators.short-circuit formatting grouping
kernel locals math math.functions math.vectors sequences sequences.repeating
sequences.repeating unicode ;
 
CONSTANT: numbers {
Line 161 ⟶ 1,410:
 
: valid-checksum? ( seq -- ? )
{ 3 1 } 12 cycle v* sum 10 mod zerodivisor? ;
 
: valid-upc? ( seq -- ? )
Line 187 ⟶ 1,436:
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
}
[ process-upc "(upside down)" "" ? "%u %s\n" printf ] each</langsyntaxhighlight>
{{out}}
<pre>
Line 203 ⟶ 1,452:
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import (
Line 328 ⟶ 1,577:
}
}
}</langsyntaxhighlight>
 
{{out}}
Line 349 ⟶ 1,598:
Implementation:
 
<langsyntaxhighlight Jlang="j">upcdigit=:".;._2]0 :0
0 0 0 1 1 0 1 NB. 0
0 0 1 1 0 0 1 NB. 1
Line 368 ⟶ 1,617:
if. 10 e.digits do._ return.end.
if.0 ~:10|digits+/ .* 12$3 1 do._ return.end.
)</langsyntaxhighlight>
 
Here, we perform some basic integrity checks and use a table lookup to identify the decimal digits.
Line 374 ⟶ 1,623:
Task example:
 
<langsyntaxhighlight Jlang="j">barcodes=:0 :0
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
# # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # #
Line 398 ⟶ 1,647:
7 0 6 4 6 6 7 4 3 0 3 0
6 5 3 4 8 3 5 4 0 4 3 5
</syntaxhighlight>
</lang>
 
The row which begins with _ is the damaged row. (If rescanning did not fix that problem, the operator would have to enter the code manually.)
 
It may be desirable to format the result differently, but that's currently not a part of the task definition.
 
=={{header|Java}}==
{{trans|Kotlin}}
<syntaxhighlight lang="java">import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
 
public class UPC {
private static final int SEVEN = 7;
 
private static final Map<String, Integer> LEFT_DIGITS = Map.of(
" ## #", 0,
" ## #", 1,
" # ##", 2,
" #### #", 3,
" # ##", 4,
" ## #", 5,
" # ####", 6,
" ### ##", 7,
" ## ###", 8,
" # ##", 9
);
 
private static final Map<String, Integer> RIGHT_DIGITS = LEFT_DIGITS.entrySet()
.stream()
.collect(Collectors.toMap(
entry -> entry.getKey()
.replace(' ', 's')
.replace('#', ' ')
.replace('s', '#'),
Map.Entry::getValue
));
 
private static final String END_SENTINEL = "# #";
private static final String MID_SENTINEL = " # # ";
 
private static void decodeUPC(String input) {
Function<String, Map.Entry<Boolean, List<Integer>>> decode = (String candidate) -> {
int pos = 0;
var part = candidate.substring(pos, pos + END_SENTINEL.length());
 
List<Integer> output = new ArrayList<>();
if (END_SENTINEL.equals(part)) {
pos += END_SENTINEL.length();
} else {
return Map.entry(false, output);
}
 
for (int i = 1; i < SEVEN; i++) {
part = candidate.substring(pos, pos + SEVEN);
pos += SEVEN;
 
if (LEFT_DIGITS.containsKey(part)) {
output.add(LEFT_DIGITS.get(part));
} else {
return Map.entry(false, output);
}
}
 
part = candidate.substring(pos, pos + MID_SENTINEL.length());
if (MID_SENTINEL.equals(part)) {
pos += MID_SENTINEL.length();
} else {
return Map.entry(false, output);
}
 
for (int i = 1; i < SEVEN; i++) {
part = candidate.substring(pos, pos + SEVEN);
pos += SEVEN;
 
if (RIGHT_DIGITS.containsKey(part)) {
output.add(RIGHT_DIGITS.get(part));
} else {
return Map.entry(false, output);
}
}
 
part = candidate.substring(pos, pos + END_SENTINEL.length());
if (!END_SENTINEL.equals(part)) {
return Map.entry(false, output);
}
 
int sum = 0;
for (int i = 0; i < output.size(); i++) {
if (i % 2 == 0) {
sum += 3 * output.get(i);
} else {
sum += output.get(i);
}
}
return Map.entry(sum % 10 == 0, output);
};
 
Consumer<List<Integer>> printList = list -> {
var it = list.iterator();
System.out.print('[');
if (it.hasNext()) {
System.out.print(it.next());
}
while (it.hasNext()) {
System.out.print(", ");
System.out.print(it.next());
}
System.out.print(']');
};
 
var candidate = input.trim();
var out = decode.apply(candidate);
if (out.getKey()) {
printList.accept(out.getValue());
System.out.println();
} else {
StringBuilder builder = new StringBuilder(candidate);
builder.reverse();
out = decode.apply(builder.toString());
if (out.getKey()) {
printList.accept(out.getValue());
System.out.println(" Upside down");
} else if (out.getValue().size() == 12) {
System.out.println("Invalid checksum");
} else {
System.out.println("Invalid digit(s)");
}
}
}
 
public static void main(String[] args) {
var barcodes = List.of(
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
);
barcodes.forEach(UPC::decodeUPC);
}
}</syntaxhighlight>
{{out}}
<pre>[9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9]
[4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0]
[8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside down
[9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside down
Invalid digit(s)
[3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside down
[2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8]
[8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside down
[7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0]
[6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5]</pre>
 
=={{header|jq}}==
'''Adapted from [[#Wren|Wren]]'''
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq'''
<syntaxhighlight lang="jq">
## Generic utilities
 
def enumerate(stream): foreach stream as $x (0; .+1; "\(.): \($x)");
def reverseString: explode | reverse | implode;
 
## UPC functions
 
def digitL: {
" ## #": 0,
" ## #": 1,
" # ##": 2,
" #### #": 3,
" # ##": 4,
" ## #": 5,
" # ####": 6,
" ### ##": 7,
" ## ###": 8,
" # ##": 9
};
 
def digitR: {
"### # ": 0,
"## ## ": 1,
"## ## ": 2,
"# # ": 3,
"# ### ": 4,
"# ### ": 5,
"# # ": 6,
"# # ": 7,
"# # ": 8,
"### # ": 9
};
 
def endSentinel: "# #"; # also at start
 
def midSentinel:" # # ";
 
def decodeUpc:
# Emit {rc: boolean, message: string}
def decodeUpcImpl:
# The "nil" result
def nil: { rc: false, message: ""};
 
(sub("^ *";"") | sub(" *$"; "")) as $upc
| if ($upc|length != 95) then nil
else [1, 3] as $oneThree
| { pos: 0,
digits: [],
sum: 0 }
| # end sentinel
if $upc[.pos: .pos+3] != endSentinel
then nil
else .pos += 3
# 6 left hand digits
| .i = 0
| until( .i == 6 or .i == null;
.i += 1
| digitL[$upc[.pos: .pos+7]] as $digit
| if ($digit|not) then .i = null
else .digits += [$digit]
| .sum += $digit * $oneThree[(.digits|length) % 2]
| .pos += 7
end )
| if .i == null then nil
else # mid sentinel
if $upc[.pos: .pos+5] != midSentinel
then nil
else .pos += 5
# 6 right hand digits
| .i = 0
| until( .i == 6 or .i == null;
.i += 1
| digitR[$upc[.pos: .pos+7]] as $digit
| if ($digit|not) then .i = null
else .digits += [$digit]
| .sum += $digit * $oneThree[(.digits|length) % 2]
| .pos += 7
end )
| if .i == null then nil
else # end sentinel
if ($upc[.pos : .pos+3] != endSentinel) then false
else if (.sum % 10 == 0)
then {rc: true, message: "\(.digits) "}
else {rc: false, message: "Failed Checksum "}
end
end
end
end
end
end
end ;
 
. as $in
| decodeUpcImpl
| .message
+ (if .rc then "Rightside Up"
else ($in | reverseString | decodeUpcImpl)
| .message
+ (if .rc then "Upside Down"
else "Invalid digit(s)"
end)
end);
 
## Examples
 
def barcodes: [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
];
 
enumerate( barcodes[] | decodeUpc )
</syntaxhighlight>
{{output}}
Essentially as for [[#Wren|Wren]].
 
 
=={{header|Julia}}==
{{trans|Raku}}
<langsyntaxhighlight lang="julia">const pat1 = (" ## #", " ## #", " # ##", " #### #", " # ##",
" ## #", " # ####", " ### ##", " ## ###", " # ##")
const pat2 = [replace(x, r"[# ]" => (x) -> x == " " ? "#" : " ") for x in pat1]
Line 445 ⟶ 1,980:
(s = decodeUPC(reverse(line))) != "" ? s : "Invalid")
end
</langsyntaxhighlight>{{out}}
<pre>
924773271019
Line 458 ⟶ 1,993:
653483540435
</pre>
 
=={{header|Kotlin}}==
{{trans|C}}
<syntaxhighlight lang="scala">val LEFT_DIGITS = mapOf(
" ## #" to 0,
" ## #" to 1,
" # ##" to 2,
" #### #" to 3,
" # ##" to 4,
" ## #" to 5,
" # ####" to 6,
" ### ##" to 7,
" ## ###" to 8,
" # ##" to 9
)
val RIGHT_DIGITS = LEFT_DIGITS.mapKeys {
it.key.replace(' ', 's').replace('#', ' ').replace('s', '#')
}
 
const val END_SENTINEL = "# #"
const val MID_SENTINEL = " # # "
 
fun decodeUPC(input: String) {
fun decode(candidate: String): Pair<Boolean, List<Int>> {
var pos = 0
var part = candidate.slice(pos until pos + END_SENTINEL.length)
if (part == END_SENTINEL) {
pos += END_SENTINEL.length
} else {
return Pair(false, emptyList())
}
 
val output = mutableListOf<Int>()
for (i in 0 until 6) {
part = candidate.slice(pos until pos + 7)
pos += 7
 
if (LEFT_DIGITS.containsKey(part)) {
output.add(LEFT_DIGITS.getOrDefault(part, -1))
} else {
return Pair(false, output.toList())
}
}
 
part = candidate.slice(pos until pos + MID_SENTINEL.length)
if (part == MID_SENTINEL) {
pos += MID_SENTINEL.length
} else {
return Pair(false, output.toList())
}
 
for (i in 0 until 6) {
part = candidate.slice(pos until pos + 7)
pos += 7
 
if (RIGHT_DIGITS.containsKey(part)) {
output.add(RIGHT_DIGITS.getOrDefault(part, -1))
} else {
return Pair(false, output.toList())
}
}
 
part = candidate.slice(pos until pos + END_SENTINEL.length)
if (part == END_SENTINEL) {
pos += END_SENTINEL.length
} else {
return Pair(false, output.toList())
}
 
val sum = output.mapIndexed { i, v -> if (i % 2 == 0) v * 3 else v }.sum()
return Pair(sum % 10 == 0, output.toList())
}
 
val candidate = input.trim()
 
var out = decode(candidate)
if (out.first) {
println(out.second)
} else {
out = decode(candidate.reversed())
if (out.first) {
print(out.second)
println(" Upside down")
} else {
if (out.second.size == 12) {
println("Invalid checksum")
} else {
println("Invalid digit(s)")
}
}
}
 
}
 
fun main() {
val barcodes = listOf(
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
)
 
for (barcode in barcodes) {
decodeUPC(barcode)
}
}</syntaxhighlight>
{{out}}
<pre>[9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9]
[4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0]
[8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside down
[9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside down
Invalid digit(s)
[3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside down
[2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8]
[8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside down
[7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0]
[6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5]</pre>
 
=={{header|Ksh}}==
<syntaxhighlight lang="ksh">#!/bin/ksh
 
# Find the corresponding UPC decimal representation of each, rejecting the error
 
# # Variables:
#
UPC_data_file="../upc.data"
END_SEQ='# #' # Start and End sequence
MID_SEQ=' # # ' # Middle sequence
typeset -a CHK_ARR=( 3 1 3 1 3 1 3 1 3 1 3 1 )
integer bpd=7 # Number of bits per digit
integer numdig=6 # Number of digits per "side"
typeset -a umess=( '' 'Upside down')
typeset -a udig=( 0001101 0011001 0010011 0111101 0100011 0110001 0101111 0111011 0110111 0001011 )
 
# # Functions:
#
 
# # Function _validate(array) - verify result with CHK_ARR
#
function _validate {
typeset _arr ; nameref _arr="$1"
typeset _ifs ; _ifs="$2"
typeset _dp _singlearr _oldIFS
 
_oldIFS=$IFS ; IFS=${_ifs}
typeset -ia _singlearr=( ${_arr[@]} )
integer _dp=$(_dotproduct _singlearr CHK_ARR)
IFS=${_oldIFS}
 
return $(( _dp % 10 ))
}
 
# # Function _dotproduct(arr1, arr2) - return dot product
#
function _dotproduct {
typeset _arr1 ; nameref _arr1="$1"
typeset _arr2 ; nameref _arr2="$2"
typeset _i _dp ; integer _i _dp
 
for (( _i=0; _i<${#_arr1[*]}; _i++ )); do
(( _dp += ( _arr1[_i] * _arr2[_i] ) ))
done
echo ${_dp}
}
 
# # Function _flipit(string) - return flipped string
#
function _flipit {
typeset _buf ; _buf="$1"
typeset _tmp ; unset _tmp
 
for (( _i=$(( ${#_buf}-1 )); _i>=0; _i-- )); do
_tmp="${_tmp}${_buf:${_i}:1}"
done
 
echo "${_tmp}"
}
 
# # Function _bitget(string, side) - return bitless string & bit
#
function _bitget {
typeset _buff ; _buff="$1"
typeset _side ; integer _side=$2
typeset _ubit _bit
 
_ubit=${_buff:0:1}
[[ ${_ubit} == \# ]] && _bit=1 || _bit=0
(( _side )) && (( _bit = ! _bit ))
 
echo ${_buff#*${_ubit}}
return ${_bit}
}
 
# # Function _decode(upc_arr, digit_arr)
#
function _decode {
typeset _uarr ; nameref _uarr="$1" # UPC code array
typeset _darr ; nameref _darr="$2" # Decimal array
 
typeset _s _d _b _bit _digit _uarrcopy ; integer _s _d _b _bit
typeset -a _uarrcopy=( ${_uarr[@]} )
 
for (( _s=0; _s<${#_uarr[*]}; _s++ )); do # each "side"
for (( _d=0; _d<numdig; _d++ )) ; do # each "digit"
for (( _b=0; _b<bpd; _b++ )) ; do # each "bit"
_uarr[_s]=$(_bitget ${_uarr[_s]} ${_s}) ; _bit=$?
_digit="${_digit}${_bit}"
done
 
_darr[_s]="${_darr[_s]} $(_todec ${_digit})"
if (( $? )); then # May be upside-down
typeset -a _uarr=( ${_uarrcopy[@]} ) # Replace
return 1
fi
unset _digit
done
done
}
 
# # Function _todec(digit) - Return numeric digit from upc code
#
function _todec {
typeset _bdig ; _bdig="$1"
typeset _i ; integer _i
 
for (( _i=0; _i<${#udig[*]}; _i++ )); do
[[ ${_bdig} == ${udig[_i]} ]] && echo ${_i} && return 0
done
return 1
}
 
# # Function _parseUPC(str, arr) - parse UPS string into 2 ele array
#
function _parseUPC {
typeset _buf ; typeset _buf="$1"
typeset _arr ; nameref _arr="$2"
typeset _pre _mid
 
_pre="${_buf%%${END_SEQ}*}"
_buf="${_buf#*${_pre}}" # Strip preamble
_buf="${_buf#*${END_SEQ}}" # Strip $SEQ
 
_arr[0]="${_buf:0:$((bpd * numdig))}" # Get the left hand digits
_buf="${_buf#*${_arr[0]}}" # Strip left side digits
 
_mid="${_buf:0:5}" # Check the middle SEQ
_buf="${_buf#*${MID_SEQ}}" # Strip $SEQ
 
_arr[1]="${_buf:0:$((bpd * numdig))}" # Get the right hand digits
_buf="${_buf#*${_arr[1]}}" # Strip right side digits
 
_end="${_buf:0:3}" # Check the end SEQ
_buf="${_buf#*${END_SEQ}}" # Strip $SEQ
}
 
######
# main #
######
 
oldIFS="$IFS" ; IFS=''
while read; do
[[ "$REPLY" == \;* ]] && continue
 
unset side_arr ; typeset -a side_arr # [0]=left [1]=right
_parseUPC "$REPLY" side_arr
 
unset digit_arr ; typeset -a digit_arr # [0]=left [1]=right
_decode side_arr digit_arr ; integer uflg=$?
if (( uflg )); then # Flip sides and reverse UPC_code
unset digit_arr ; typeset -a digit_arr # [0]=left [1]=right
buff="$(_flipit "${side_arr[0]}")"
side_arr[0]="$(_flipit "${side_arr[1]}")"
side_arr[1]="${buff}"
_decode side_arr digit_arr ; integer vflg=$?
fi
 
(( ! vflg )) && _validate digit_arr "${oldIFS}" ; integer vflg=$?
if (( vflg )); then
print "INVALID DIGIT(S)"
unset vflg
else
print "${digit_arr[*]} ${umess[uflg]}"
unset uflg
fi
 
done < ${UPC_data_file}</syntaxhighlight>
{{out}}
<pre> 9 2 4 7 7 3 2 7 1 0 1 9
4 0 3 9 4 4 4 4 1 0 5 0
8 3 4 9 9 9 6 7 6 7 0 6 Upside down
9 3 9 8 2 5 1 5 8 8 1 1 Upside down
INVALID DIGIT(S)
3 1 6 3 1 3 7 1 8 7 1 7 Upside down
2 1 4 5 7 5 8 7 5 6 0 8
8 1 8 7 7 8 8 4 1 8 1 3 Upside down
7 0 6 4 6 6 7 4 3 0 3 0
6 5 3 4 8 3 5 4 0 4 3 5</pre>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">s=" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
# # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # #
# # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # #
# # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # #
# # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # #
# # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # #
# # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # #
# # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # #
# # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # #
# # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ";
s//=Characters[StringSplit[#,"\n"]]&;
s=%/.{"#"->0," "->1};
Map[BarcodeRecognize@*Image@*List][s]</syntaxhighlight>
{{out}}
<pre>{"924773271019", "403944441050", "834999676706", "939825158811", {}, "316313718717", "214575875608", "818778841813", "706466743030", "653483540435"}</pre>
 
=={{header|Nim}}==
{{trans|D}}
<syntaxhighlight lang="nim">import algorithm, sequtils, strutils
 
const
 
LeftDigits = [" ## #",
" ## #",
" # ##",
" #### #",
" # ##",
" ## #",
" # ####",
" ### ##",
" ## ###",
" # ##"]
 
RightDigits = LeftDigits.mapIt(it.multiReplace(("#", " "), (" ", "#")))
 
EndSentinel = "# #"
MidSentinel = " # # "
 
 
template isEven(n: int): bool = (n and 1) == 0
 
 
proc decodeUPC(input: string) =
 
#.................................................................................................
 
proc decode(candidate: string): tuple[valid: bool, list: seq[int]] =
 
const Invalid = (false, @[])
 
var pos = 0
var next = pos + EndSentinel.len
if candidate[pos..<next] == EndSentinel:
pos = next
else:
return Invalid
 
for _ in 1..6:
next = pos + 7
let i = LeftDigits.find(candidate[pos..<next])
if i >= 0:
result.list.add i
pos = next
else:
return Invalid
 
next = pos + MidSentinel.len
if candidate[pos..<next] == MidSentinel:
pos = next
else:
return Invalid
 
for _ in 1..6:
next = pos + 7
let i = RightDigits.find(candidate[pos..<next])
if i >= 0:
result.list.add i
pos = next
else:
return Invalid
 
next = pos + EndSentinel.len
if candidate[pos..<next] == EndSentinel:
pos = next
else:
return Invalid
 
var sum = 0
for i, v in result.list:
sum += (if i.isEven: 3 * v else: v)
result.valid = sum mod 10 == 0
 
#.................................................................................................
 
var candidate = input.strip()
let output = candidate.decode()
if output.valid:
echo output.list.join(", ")
else:
candidate.reverse()
let output = candidate.decode()
if output.valid:
echo output.list.join(", "), " Upside down"
elif output.list.len == 0:
echo "Invalid digit(s)"
else:
echo "Invalid checksum: ", output.list.join(", ")
 
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
when isMainModule:
 
const BarCodes = [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
]
 
for barcode in BarCodes:
barcode.decodeUPC()</syntaxhighlight>
 
{{out}}
<pre>9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9
4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0
8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6 Upside down
9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1 Upside down
Invalid digit(s)
3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7 Upside down
2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8
8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3 Upside down
7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0
6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5</pre>
 
=={{header|Perl}}==
{{trans|Raku}}
<langsyntaxhighlight lang="perl">use strict;
use warnings;
use feature 'say';
Line 509 ⟶ 2,489:
// 'Invalid';
}
</syntaxhighlight>
</lang>
{{out}}
<pre>924773271019
Line 523 ⟶ 2,503:
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>constant numbers = {" ## #", -- 0
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
" ## #", -- 1
<span style="color: #008080;">constant</span> <span style="color: #000000;">numbers</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">" ## #"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 0</span>
" # ##", -- 2
<span style="color: #008000;">" ## #"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 31</span>
<span style="color: #008000;">" # ##"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 42</span>
<span style="color: #008000;">" #### #"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 53</span>
<span style="color: #008000;">" # ##"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 64</span>
<span style="color: #008000;">" ## #"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 75</span>
<span style="color: #008000;">" # ####"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 86</span>
<span style="color: #008000;">" ### ##"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 7</span>
" # ##"} -- 9
<span style="color: #008000;">" ## ###"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- 8</span>
 
<span style="color: #008000;">" # ##"</span><span style="color: #0000FF;">}</span> <span style="color: #000080;font-style:italic;">-- 9</span>
procedure decode(string bar_code)
bar_code = trim(bar_code)
<span style="color: #008080;">procedure</span> <span style="color: #000000;">decode</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">bar_code</span><span style="color: #0000FF;">)</span>
if length(bar_code)=95
<span style="color: #000000;">bar_code</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bar_code</span><span style="color: #0000FF;">)</span>
and bar_code[1..3]="# #"
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bar_code</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">95</span>
and bar_code[46..50]=" # # "
<span style="color: #008080;">and</span> <span style="color: #000000;">bar_code</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">3</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">"# #"</span>
and bar_code[93..95]="# #" then
<span style="color: #008080;">and</span> <span style="color: #000000;">bar_code</span><span style="color: #0000FF;">[</span><span style="color: #000000;">46</span><span style="color: #0000FF;">..</span><span style="color: #000000;">50</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">" # # "</span>
for reversed=false to true do
<span style="color: #008080;">and</span> <span style="color: #000000;">bar_code</span><span style="color: #0000FF;">[</span><span style="color: #000000;">93</span><span style="color: #0000FF;">..</span><span style="color: #000000;">95</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">"# #"</span> <span style="color: #008080;">then</span>
sequence r = {}
<span style="color: #008080;">for</span> <span style="color: #000000;">reversed</span><span style="color: #0000FF;">=</span><span style="color: #004600;">false</span> <span style="color: #008080;">to</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
for i=1 to 12 do
<span style="color: #004080;">sequence</span> <span style="color: #000000;">r</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
integer st = iff(i<=6?i*7-3:i*7+2)
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">12</span> <span style="color: #008080;">do</span>
string number = bar_code[st..st+6]
<span style="color: #004080;">integer</span> <span style="color: #000000;">st</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">6</span><span style="color: #0000FF;">?</span><span style="color: #000000;">i</span><span style="color: #0000FF;">*</span><span style="color: #000000;">7</span><span style="color: #0000FF;">-</span><span style="color: #000000;">3</span><span style="color: #0000FF;">:</span><span style="color: #000000;">i</span><span style="color: #0000FF;">*</span><span style="color: #000000;">7</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span>
if i>6 then number = substitute_all(number," #X","X #") end if
<span style="color: #004080;">string</span> <span style="color: #000000;">number</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">bar_code</span><span style="color: #0000FF;">[</span><span style="color: #000000;">st</span><span style="color: #0000FF;">..</span><span style="color: #000000;">st</span><span style="color: #0000FF;">+</span><span style="color: #000000;">6</span><span style="color: #0000FF;">]</span>
r &= find(number,numbers)-1
<span style="color: #008080;">if</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">></span><span style="color: #000000;">6</span> <span style="color: #008080;">then</span> <span style="color: #000000;">number</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute_all</span><span style="color: #0000FF;">(</span><span style="color: #000000;">number</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" #X"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"X #"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end for
<span style="color: #000000;">r</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">number</span><span style="color: #0000FF;">,</span><span style="color: #000000;">numbers</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span>
if not find(-1,r) then
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
if remainder(sum(sq_mul(r,{3,1,3,1,3,1,3,1,3,1,3,1})),10) then
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
printf(1,"invalid checksum\n")
<span style="color: #008080;">if</span> <span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">r</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">})),</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
else
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"invalid checksum\n"</span><span style="color: #0000FF;">)</span>
printf(1,"%v%s\n",{r,iff(reversed?" (upside down)","")})
end if<span style="color: #008080;">else</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%v%s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">r</span><span style="color: #0000FF;">,</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">reversed</span><span style="color: #0000FF;">?</span><span style="color: #008000;">" (upside down)"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)})</span>
return
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end if
bar_code <span style="color: reverse(bar_code)#008080;">return</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end for
<span style="color: #000000;">bar_code</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bar_code</span><span style="color: #0000FF;">)</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
printf(1,"invalid\n")
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end procedure
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"invalid\n"</span><span style="color: #0000FF;">)</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
constant bar_codes = split("""
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
<span style="color: #008080;">constant</span> <span style="color: #000000;">bar_codes</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"""
# # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # #
# # # ## # ### # ## ### ## # ## # # # ### # # # ## ## # ## # ## ## # ## # ## # #### ### ## # #
# # ## ## ## ## # #### # # ## ### # ## # ## # # # ## ## # ### ## ## # ## # # ### ## # # # #
# # ### ## # # ## ## ### # # # # # # # # # # ## # ## # ## ## ## # # ### # ## ## # # # #
# # ## # # ## ## # # # # # ### # ## # # # # # ## ## # ## # #### #### # # ## # ### ## # # #
# # # ## # ## # # ## ## # ### ## ## # # # # # # ## # # ### # ### ## # # ### # ## ## # # #
# # # # ## ## # # # # ## ## ### # # # # # ### ## ## # ## # # #### ### # # # ## # ## ## # #
# # ### ## ## # # ## ## # # ## # ## ## # ### # # # # # # ### # # # ### # # # ### # # #
# # # #### ## # ## ## # # # ## ## ### #### # # # # # ### ## ### ### ## ### ### ## # # # ### ## # #
# # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # #
""","\n",true)
# # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
for i=1 to length(bar_codes) do
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span>
decode(bar_codes[i])
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bar_codes</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
end for</lang>
<span style="color: #000000;">decode</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bar_codes</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Line 592 ⟶ 2,575:
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de l2n (Lst)
(case Lst
((0 0 0 1 1 0 1) 0)
Line 645 ⟶ 2,628:
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # " ) )
(for L (mapcar upc *U)
(println (if (car L) @ 'invalid)) )</langsyntaxhighlight>
{{out}}
<pre>
Line 659 ⟶ 2,642:
(6 5 3 4 8 3 5 4 0 4 3 5)
</pre>
 
=={{header|Python}}==
<syntaxhighlight lang="python">"""UPC-A barcode reader. Requires Python =>3.6"""
import itertools
import re
 
RE_BARCODE = re.compile(
r"^(?P<s_quiet> +)" # quiet zone
r"(?P<s_guard># #)" # start guard
r"(?P<left>[ #]{42})" # left digits
r"(?P<m_guard> # # )" # middle guard
r"(?P<right>[ #]{42})" # right digits
r"(?P<e_guard># #)" # end guard
r"(?P<e_quiet> +)$" # quiet zone
)
 
LEFT_DIGITS = {
(0, 0, 0, 1, 1, 0, 1): 0,
(0, 0, 1, 1, 0, 0, 1): 1,
(0, 0, 1, 0, 0, 1, 1): 2,
(0, 1, 1, 1, 1, 0, 1): 3,
(0, 1, 0, 0, 0, 1, 1): 4,
(0, 1, 1, 0, 0, 0, 1): 5,
(0, 1, 0, 1, 1, 1, 1): 6,
(0, 1, 1, 1, 0, 1, 1): 7,
(0, 1, 1, 0, 1, 1, 1): 8,
(0, 0, 0, 1, 0, 1, 1): 9,
}
 
RIGHT_DIGITS = {
(1, 1, 1, 0, 0, 1, 0): 0,
(1, 1, 0, 0, 1, 1, 0): 1,
(1, 1, 0, 1, 1, 0, 0): 2,
(1, 0, 0, 0, 0, 1, 0): 3,
(1, 0, 1, 1, 1, 0, 0): 4,
(1, 0, 0, 1, 1, 1, 0): 5,
(1, 0, 1, 0, 0, 0, 0): 6,
(1, 0, 0, 0, 1, 0, 0): 7,
(1, 0, 0, 1, 0, 0, 0): 8,
(1, 1, 1, 0, 1, 0, 0): 9,
}
 
 
MODULES = {
" ": 0,
"#": 1,
}
 
DIGITS_PER_SIDE = 6
MODULES_PER_DIGIT = 7
 
 
class ParityError(Exception):
"""Exception raised when a parity error is found."""
 
 
class ChecksumError(Exception):
"""Exception raised when check digit does not match."""
 
 
def group(iterable, n):
"""Chunk the iterable into groups of size ``n``."""
args = [iter(iterable)] * n
return tuple(itertools.zip_longest(*args))
 
 
def parse(barcode):
"""Return the 12 digits represented by the given barcode. Raises a
ParityError if any digit fails the parity check."""
match = RE_BARCODE.match(barcode)
 
# Translate bars and spaces to 1s and 0s so we can do arithmetic
# with them. Group "modules" into chunks of 7 as we go.
left = group((MODULES[c] for c in match.group("left")), MODULES_PER_DIGIT)
right = group((MODULES[c] for c in match.group("right")), MODULES_PER_DIGIT)
 
# Parity check
left, right = check_parity(left, right)
 
# Lookup digits
return tuple(
itertools.chain(
(LEFT_DIGITS[d] for d in left),
(RIGHT_DIGITS[d] for d in right),
)
)
 
 
def check_parity(left, right):
"""Check left and right parity. Flip left and right if the barcode
was scanned upside down."""
# When reading from left to right, each digit on the left should
# have odd parity, and each digit on the right should have even
# parity.
left_parity = sum(sum(d) % 2 for d in left)
right_parity = sum(sum(d) % 2 for d in right)
 
# Use left and right parity to check if the barcode was scanned
# upside down. Flip it if it was.
if left_parity == 0 and right_parity == DIGITS_PER_SIDE:
_left = tuple(tuple(reversed(d)) for d in reversed(right))
right = tuple(tuple(reversed(d)) for d in reversed(left))
left = _left
elif left_parity != DIGITS_PER_SIDE or right_parity != 0:
# Error condition. Mixed parity.
error = tuple(
itertools.chain(
(LEFT_DIGITS.get(d, "_") for d in left),
(RIGHT_DIGITS.get(d, "_") for d in right),
)
)
raise ParityError(" ".join(str(d) for d in error))
 
return left, right
 
 
def checksum(digits):
"""Return the check digit for the given digits. Raises a
ChecksumError if the check digit does not match."""
odds = (digits[i] for i in range(0, 11, 2))
evens = (digits[i] for i in range(1, 10, 2))
 
check_digit = (sum(odds) * 3 + sum(evens)) % 10
 
if check_digit != 0:
check_digit = 10 - check_digit
 
if digits[-1] != check_digit:
raise ChecksumError(str(check_digit))
 
return check_digit
 
 
def main():
barcodes = [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### ## ## # ### # # ",
]
 
for barcode in barcodes:
try:
digits = parse(barcode)
except ParityError as err:
print(f"{err} parity error!")
continue
 
try:
check_digit = checksum(digits)
except ChecksumError as err:
print(f"{' '.join(str(d) for d in digits)} checksum error! ({err})")
continue
 
print(f"{' '.join(str(d) for d in digits)}")
 
 
if __name__ == "__main__":
main()
</syntaxhighlight>
 
{{out}}
<pre>
$ python barcode.py
9 2 4 7 7 3 2 7 1 0 1 9
4 0 3 9 4 4 4 4 1 0 5 0
8 3 4 9 9 9 6 7 6 7 0 6
9 3 9 8 2 5 1 5 8 8 1 1
7 4 8 1 5 9 9 2 3 9 2 _ parity error!
3 1 6 3 1 3 7 1 8 7 1 7
2 1 4 5 7 5 8 7 5 6 0 8
8 1 8 7 7 8 8 4 1 8 1 3
7 0 6 4 6 6 7 4 3 0 3 0
6 5 3 4 8 3 5 4 0 4 3 5
6 5 3 4 8 3 5 4 0 4 2 5 checksum error! (8)
</pre>
 
=={{header|Racket}}==
 
<syntaxhighlight lang="racket">#lang racket
 
;; inspired by Kotlin
 
(define (is-#? c) (char=? c #\#))
 
(define left-digits
(for/hash ((i (in-naturals))
(c '((#f #f #t #t #f)
(#f #t #t #f #f)
(#f #t #f #f #t)
(#t #t #t #t #f)
(#t #f #f #f #t)
(#t #t #f #f #f)
(#t #f #t #t #t)
(#t #t #t #f #t)
(#t #t #f #t #t)
(#f #f #t #f #t))))
(values `(#f ,@c #t) i)))
 
(define right-digits (for/hash (([k v] left-digits)) (values (map not k) v)))
 
(define (lookup-blocks bits hsh fail)
(let recur ((bs bits) (r null))
(if (null? bs)
(reverse r)
(let-values (((bs′ tl) (split-at bs 7)))
(let ((d (hash-ref hsh bs′ (λ () (fail (list 'not-found bs′))))))
(recur tl (cons d r)))))))
 
(define (extract-blocks b fail)
(let*-values
(((e-l-m-r-e) (map is-#? (string->list (string-trim b))))
((_) (unless (= (length e-l-m-r-e) (+ 3 (* 7 6) 5 (* 7 6) 3))
(fail 'wrong-length)))
((e l-m-r-e) (split-at e-l-m-r-e 3))
((_) (unless (equal? e '(#t #f #t)) (fail 'left-sentinel)))
((l-m-r e) (split-at-right l-m-r-e 3))
((_) (unless (equal? e '(#t #f #t)) (fail 'right-sentinel)))
((l m-r) (split-at l-m-r 42))
((m r) (split-at m-r 5))
((_) (unless (equal? m '(#f #t #f #t #f)) (fail 'mid-sentinel))))
(values l r)))
 
(define (upc-checksum? ds)
(zero? (modulo (for/sum ((m (in-cycle '(3 1))) (d ds)) (* m d)) 10)))
 
(define (lookup-digits l r fail (transform values))
(let/ec fail-lookups
(define ds (append (lookup-blocks l left-digits (λ _ (fail-lookups #f)))
(lookup-blocks r right-digits (λ _ (fail-lookups #f)))))
(if (upc-checksum? ds)
(transform ds)
(fail (list 'checksum (transform ds))))))
 
(define (decode-upc barcode upside-down fail)
(define-values (l r) (extract-blocks barcode fail))
(or (lookup-digits l r fail)
(lookup-digits (reverse r) (reverse l) fail upside-down)))
 
(define (report-upc barcode)
(displayln (decode-upc barcode
(λ (v) (cons 'upside-down v))
(λ (e) (format "invalid: ~s" e)))))
 
(define (UPC)
(for-each report-upc
'(" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # "
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # "
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # "
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # "
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # "
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # "
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # "
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # "
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # "
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
; first element again, with corrupted second digit
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ")))
 
(module+ main (UPC))</syntaxhighlight>
 
{{out}}
 
<pre>(9 2 4 7 7 3 2 7 1 0 1 9)
(4 0 3 9 4 4 4 4 1 0 5 0)
(upside-down 8 3 4 9 9 9 6 7 6 7 0 6)
(upside-down 9 3 9 8 2 5 1 5 8 8 1 1)
#f
(upside-down 3 1 6 3 1 3 7 1 8 7 1 7)
(2 1 4 5 7 5 8 7 5 6 0 8)
(upside-down 8 1 8 7 7 8 8 4 1 8 1 3)
(7 0 6 4 6 6 7 4 3 0 3 0)
(6 5 3 4 8 3 5 4 0 4 3 5)
invalid: (checksum (9 9 4 7 7 3 2 7 1 0 1 9))</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
<syntaxhighlight lang="raku" perl6line>sub decode_UPC ( Str $line ) {
constant @patterns1 = ' ## #', ' ## #', ' # ##', ' #### #', ' # ##',
' ## #', ' # ####', ' ### ##', ' ## ###', ' # ##';
Line 701 ⟶ 2,965:
// decode_UPC($line.flip)
// 'Invalid';
}</langsyntaxhighlight>
 
{{out}}
Line 716 ⟶ 2,980:
 
=={{header|REXX}}==
<langsyntaxhighlight lang="rexx">/*REXX program to read/interpret UPC symbols and translate them to a numberic string.*/
#.0= ' ## #'
#.1= ' ## #'
Line 726 ⟶ 2,990:
#.7= ' ### ##'
#.8= ' ## ###' /* [↓] right─sided UPC digits.*/
#.9= ' # ##' ; do i=0 for 10; ##.i= translate(#.i, ' #', "# ")
end /*i*/
say center('UPC', 14, "─") ' ___---'copies(1234567, 6)"-----"copies(1234567, 6)'___---'
@.=.
@.1 = ' # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # '
Line 740 ⟶ 3,004:
@.9 = ' # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # '
@.10= ' # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # '
ends= '# #' /*define ENDS literal const*/
do j=1 while @.j\==.; $= @.j
txt= /*initialize TXT variable*/
if left($, 9)\='' | right($, 9)\='' then txt= 'bad blanks'
$= strip($); $$= $ /*elide blanks at ends of $*/
L= length($) /*obtain length of $ string*/
if left($, 3) \==ends | right($, 3) \==ends then txt= 'bad fence'
if L \== 95 & txt=='' then txt= 'bad length'
$= substr($, 4, L - length(ends)*2) /*elide "ends". */
$= delstr($, length($) % 2 - 1, 5) /* " middle. */
sum= 0 /*initialize SUM*/
if txt=='' then do k=1 for 12; parse var $ x +7 $ /*get UPC digit.*/
do d=0 for 10; if x==#.d | x==##.d then leave /*valid digit? */
end /*d*/
if d==10 & k \==12 then do; txt= 'reversed' ; leave; end
if d==10 then do; txt= 'bad digit'; leave; end
if k // 2 then sum= sum + d * 3 /*mult. by 3. */
else sum= sum + d /* " " 1. */
txt= txt || d
end /*k*/
 
if do j=left(txt,1 while @.j)\==.;"b" then if sum//10\==0 then $txt= @.j;'bad checksum' /*invalid sum? txt=*/
say center( if leftstrip($txt), 915)\= ' ' $$ | right($, 9)\='' /*show thenchksum txt=(or 'baderr blanks'msg) with the UPC*/
end /*j*/ /*stick a fork in it, we're all done. */</syntaxhighlight>
$= strip($); $$= $; L= length($)
if left($, 3)\=='# #' | right($, 3)\=="# #" then txt= 'bad fence'
if L\==95 & txt=='' then txt= 'bad len'
$= substr($, 4, L - 3 - 3); sum= 0 /*elide ends.*/
$= delstr($, length($) % 2 - 1, 5) /* " middle.*/
if txt=='' then do k=1 for 12; parse var $ x +7 $ /*get UPC dig.*/
do d=0 for 10; if x==#.d | x==##.d then leave /*valid digit?*/
end /*d*/
if d==10 & k\==12 then do; txt= 'reversed'; leave; end
if d==10 then do; txt= 'bad dig'; leave; end
if k//2 then sum= sum + 3 * d /*mult. by 3.*/
else sum= sum + d /* " " 1.*/
txt= txt || d
end /*k*/
if sum//10 \== 0 then txt= 'bad chksum' /*invalid sum?*/
say center( strip(txt), 15) ' ' $$
end /*j*/ /*stick a fork in it, we're all done. */</lang>
{{out|output|text=&nbsp; when using the internal default input:}}
<pre>
─────UPC────── ---123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567---
─────UPC────── ___123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567___
924773271019 # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # #
403944441050 # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # #
reversed # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # #
reversed # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # #
bad dig digit # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # #
reversed # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # #
214575875608 # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # #
reversed # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # #
706466743030 # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # #
bad len length # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
</pre>
 
=={{header|Ruby}}==
{{trans|C}}
<syntaxhighlight lang="ruby">DIGIT_F = {
" ## #" => 0,
" ## #" => 1,
" # ##" => 2,
" #### #" => 3,
" # ##" => 4,
" ## #" => 5,
" # ####" => 6,
" ### ##" => 7,
" ## ###" => 8,
" # ##" => 9,
}
 
DIGIT_R = {
"### # " => 0,
"## ## " => 1,
"## ## " => 2,
"# # " => 3,
"# ### " => 4,
"# ### " => 5,
"# # " => 6,
"# # " => 7,
"# # " => 8,
"### # " => 9,
}
 
END_SENTINEL = "# #"
MID_SENTINEL = " # # "
 
def decode_upc(s)
def decode_upc_impl(input)
upc = input.strip
if upc.length != 95 then
return false
end
 
pos = 0
digits = []
sum = 0
 
# end sentinel
if upc[pos .. pos + 2] == END_SENTINEL then
pos += 3
else
return false
end
 
# 6 left hand digits
for i in 0 .. 5
digit = DIGIT_F[upc[pos .. pos + 6]]
if digit == nil then
return false
else
digits.push(digit)
sum += digit * [1, 3][digits.length % 2]
pos += 7
end
end
 
# mid sentinel
if upc[pos .. pos + 4] == MID_SENTINEL then
pos += 5
else
return false
end
 
# 6 right hand digits
for i in 0 .. 5
digit = DIGIT_R[upc[pos .. pos + 6]]
if digit == nil then
return false
else
digits.push(digit)
sum += digit * [1, 3][digits.length % 2]
pos += 7
end
end
 
# end sentinel
if upc[pos .. pos + 2] == END_SENTINEL then
pos += 3
else
return false
end
 
if sum % 10 == 0 then
print digits, " "
return true
else
print "Failed Checksum "
return false
end
end
 
if decode_upc_impl(s) then
puts "Rightside Up"
elsif decode_upc_impl(s.reverse) then
puts "Upside Down"
else
puts "Invalid digit(s)"
end
end
 
def main
num = 0
 
print "%2d: " % [num += 1]
decode_upc(" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ")
 
print "%2d: " % [num += 1]
decode_upc(" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ")
end
 
main()</syntaxhighlight>
{{out}}
<pre> 1: [9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9] Rightside Up
2: [4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0] Rightside Up
3: [8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside Down
4: [9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside Down
5: Invalid digit(s)
6: [3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside Down
7: [2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8] Rightside Up
8: [8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside Down
9: [7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0] Rightside Up
10: [6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5] Rightside Up</pre>
 
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
'read UPC barcode Antoni Gual 10/2022 https://rosettacode.org/wiki/UPC
 
Option Explicit
Const m_limit ="# #"
Const m_middle=" # # "
Dim a,bnum,i,check,odic
a=array(" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",_
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",_
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",_
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",_
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",_
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",_
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",_
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",_
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",_
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ")
 
' 0 1 2 3 4 5 6 7 8 9
bnum=Array("0001101","0011001","0010011","0111101","0100011"," 0110001","0101111","0111011","0110111","0001011")
 
Set oDic = WScript.CreateObject("scripting.dictionary")
For i=0 To 9:
odic.Add bin2dec(bnum(i),Asc("1")),i+1
odic.Add bin2dec(bnum(i),Asc("0")),-i-1
Next
 
For i=0 To UBound(a) : print pad(i+1,-2) & ": "& upc(a(i)) :Next
WScript.Quit(1)
Function bin2dec(ByVal B,a) 'binary,ascii of bit 1
Dim n
While len(b)
n =n *2 - (asc(b)=a) 'true is -1 in vbs
b=mid(b,2)
Wend
bin2dec= n And 127
End Function
Sub print(s):
On Error Resume Next
WScript.stdout.WriteLine (s)
If err= &h80070006& Then WScript.Echo " Please run this script with CScript": WScript.quit
End Sub
function pad(s,n) if n<0 then pad= right(space(-n) & s ,-n) else pad= left(s& space(n),n) end if :end function
Function iif(t,a,b) If t Then iif=a Else iif=b End If :End Function
Function getnum(s,r) 'get a number from code, check if its's reversed and trim the code
Dim n,s1,r1
'returns number or 0 if not found
s1=Left(s,7)
s=Mid(s,8)
r1=r
Do
If r Then s1=StrReverse(s1)
n=bin2dec(s1,asc("#"))
If odic.exists(n) Then
getnum=odic(n)
Exit Function
Else
If r1<>r Then getnum=0:Exit Function
r=Not r
End If
Loop
End Function
Function getmarker(s,m) 'get a marker and trim the code
getmarker= (InStr(s,m)= 1)
s=Mid(s,Len(m)+1)
End Function
Function checksum(ByVal s)
Dim n,i : n=0
do
n=n+(Asc(s)-48)*3
s=Mid(s,2)
n=n+(Asc(s)-48)*1
s=Mid(s,2)
Loop until Len(s)=0
checksum= ((n mod 10)=0)
End function
Function upc(ByVal s1)
Dim i,n,s,out,rev,j
'forget about the leading adn trailing spaces, the task says they may be wrong
s=Trim(s1)
If getmarker(s,m_limit)=False Then upc= "bad start marker ":Exit function
rev=False
out=""
For j= 0 To 1
For i=0 To 5
n=getnum(s,rev)
If n=0 Then upc= pad(out,16) & pad ("bad code",-10) & pad("pos "& i+j*6+1,-11): Exit Function
out=out & Abs(n)-1
Next
If j=0 Then If getmarker(s,m_middle)=False Then upc= "bad middle marker " & out :Exit Function
Next
If getmarker(s,m_limit)=False Then upc= "bad end marker " :Exit function
If rev Then out=strreverse(out)
upc= pad(out,16) & pad(iif (checksum(out),"valid","not valid"),-10)& pad(iif(rev,"reversed",""),-11)
End Function
</syntaxhighlight>
{{out}}
<small>
<pre>
 
1: 924773271019 valid
2: 403944441050 valid
3: 834999676706 valid reversed
4: 939825158811 valid reversed
5: 74815992392 bad code pos 12
6: 316313718717 valid reversed
7: 214575875608 valid
8: 818778841813 valid reversed
9: 706466743030 valid
10: 653483540435 valid
</pre>
</small>
 
 
=={{header|Wren}}==
{{trans|Ruby}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./fmt" for Fmt
 
var digitL = {
" ## #": 0,
" ## #": 1,
" # ##": 2,
" #### #": 3,
" # ##": 4,
" ## #": 5,
" # ####": 6,
" ### ##": 7,
" ## ###": 8,
" # ##": 9
}
 
var digitR = {
"### # ": 0,
"## ## ": 1,
"## ## ": 2,
"# # ": 3,
"# ### ": 4,
"# ### ": 5,
"# # ": 6,
"# # ": 7,
"# # ": 8,
"### # ": 9
}
 
var endSentinel = "# #" // also at start
var midSentinel = " # # "
 
var decodeUpc = Fn.new { |s|
var decodeUpcImpl = Fn.new { |input|
var upc = input.trim()
if (upc.count != 95) return false
var pos = 0
var digits = []
var sum = 0
var oneThree = [1, 3]
 
// end sentinel
if (upc[pos..pos+2] == endSentinel) {
pos = pos + 3
} else {
return false
}
 
// 6 left hand digits
for (i in 0..5) {
var digit = digitL[upc[pos..pos+6]]
if (!digit) return false
digits.add(digit)
sum = sum + digit * oneThree[digits.count % 2]
pos = pos + 7
}
 
// mid sentinel
if (upc[pos..pos+4] == midSentinel) {
pos = pos + 5
} else {
return false
}
 
// 6 right hand digits
for (i in 0..5) {
var digit = digitR[upc[pos..pos+6]]
if (!digit) return false
digits.add(digit)
sum = sum + digit * oneThree[digits.count % 2]
pos = pos + 7
}
 
// end sentinel
if (upc[pos..pos+2] != endSentinel) return false
 
if (sum%10 == 0) {
System.write("%(digits) ")
return true
}
System.write("Failed Checksum ")
return false
}
 
if (decodeUpcImpl.call(s)) {
System.print("Rightside Up")
} else if (decodeUpcImpl.call(s[-1..0])) {
System.print("Upside Down")
} else {
System.print("Invalid digit(s)")
}
}
 
var barcodes = [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ",
" # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ",
" # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ",
" # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ",
" # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ",
" # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ",
" # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ",
" # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
]
var n = 0
for (barcode in barcodes) {
n = n + 1
Fmt.write("$2d: ", n)
decodeUpc.call(barcode)
}</syntaxhighlight>
 
{{out}}
<pre>
1: [9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9] Rightside Up
2: [4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0] Rightside Up
3: [8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside Down
4: [9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside Down
5: Invalid digit(s)
6: [3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside Down
7: [2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8] Rightside Up
8: [8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside Down
9: [7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0] Rightside Up
10: [6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5] Rightside Up
</pre>
 
=={{header|zkl}}==
<syntaxhighlight lang="zkl">var lhd=Dictionary(), rhd=Dictionary();
[0..].zip(List(
"0 0 0 1 1 0 1", //--> "___##_#":0 "###__#_":0
"0 0 1 1 0 0 1",
"0 0 1 0 0 1 1",
"0 1 1 1 1 0 1",
"0 1 0 0 0 1 1",
"0 1 1 0 0 0 1",
"0 1 0 1 1 1 1",
"0 1 1 1 0 1 1",
"0 1 1 0 1 1 1",
"0 0 0 1 0 1 1") //--> "___#_##":9 "###_#__":9
).pump(Void,fcn([(n,bs)]){
bs-=" ";
lhd[bs.translate("01","_#")]=n;
rhd[bs.translate("10","_#")]=n;
});
 
fcn parseBarCode(barcode, one=True){ // --> 12 digits
upsideDown:='wrap{ // was I looking at this bar code upside down?
if(one and (r:=parseBarCode(barcode.reverse(),False))) return(r);
return(False);
};
 
var [const] start=RegExp(String("_"*9, "+#_#")), tail="_"*7;
if(not start.search(barcode)) return(upsideDown());
r,idx,d,mark := List(), start.matched[0][1], lhd, "_#_#_";
do(2){
do(6){
if(Void==(z:=d.find(barcode[idx,7]))) return(upsideDown());
r.append(z);
idx+=7;
}
if(barcode[idx,5] != mark) return(Void);
d,idx,mark = rhd, idx+5, "#_#__";
}
if(tail!=barcode[idx,7]) return(Void); // 9 trailing blanks? two checked above
r
}</syntaxhighlight>
Or, if you like way too long regular expressions:
<syntaxhighlight lang="zkl">var upcRE = RegExp(String("_"*9, "+#_#",
lhd.keys.concat("|","(",")")*6, "_#_#_",
rhd.keys.concat("|","(",")")*6, "#_#", "_"*9)),
digits=lhd.copy().extend(rhd);
 
fcn parseBarCode2(barcode){ // --> 12 digits
if(not (upcRE.search(barcode) or upcRE.search(barcode.reverse()))) return(False);
upcRE.matched[1,*] // ( (a,b), "_#_####","_##___#", 10 more digit patterns )
.apply(digits.get)
}</syntaxhighlight>
<syntaxhighlight lang="zkl">barcodes:=
#<<<"
_________#_#___#_##__#__##_#___##_###_##_###_##_####_#_#_#_##_##__#___#__##__##_###__#_##__##_###_#__#_#_________
_________#_#_#___##___##_#_####_#___#_##_#___##_#___##_#_#_#_###__#_###__##__##_###__#_#__###_###__#_#_#_________
_________#_#____#_#_#__###__#___#____#_#__#___#____#_#_#_#_##_#___##_#___##_#___##___#_#_####_###_##_#_#_________
_________#_#_##__##_##__##___#__#___#__#_###__#_##__##_#_#_#___##_##__#__###_##_##_#___#_####_##_#___#_#_________
_________#_#_###_##_#___##_##_###__##__#_##___#___#_##_#_#_###_#__##_##__#____#_###_#__##_##__#______#_#__________
__________#_#__#___#_##__##__#___#___#__#_##__##__#___#_#_#_#_####_#__##__#_####_####_#_#__##__#_####_#_#____________
_________#_#__#__##__##__#_#___##_##___#_###_##_##___#_#_#_#__#___#___#__#__###_#_#____###__#_#__#___#_#_________
_________#_#_#____#_##__##___#__#_##__##__###_#___#__#_#_#_###_##_##_###_##_###_###_##_#__##__###_##_#_#__________
_________#_#_###_##___##_#_#_####_#___##_#_####_#_####_#_#_#___#__#_###__#____#_###__#_#____#_###__#_#_#_________
_______________#_#_#_####_##___#_####_#_#___##_##_###_####_#_#_#_#__###_#_###__###__#_#_###__#____#_#__###_#_#_________"
.split("\n");
#<<<
 
foreach n,barcode in ([1..].zip(barcodes)){
bc:=parseBarCode(barcode);
println("%2d: [%s]".fmt(n,bc and bc.concat(" ") or "Not valid"));
}</syntaxhighlight>
{{out}}
<pre>
1: [9 2 4 7 7 3 2 7 1 0 1 9]
2: [4 0 3 9 4 4 4 4 1 0 5 0]
3: [8 3 4 9 9 9 6 7 6 7 0 6]
4: [9 3 9 8 2 5 1 5 8 8 1 1]
5: [Not valid]
6: [3 1 6 3 1 3 7 1 8 7 1 7]
7: [2 1 4 5 7 5 8 7 5 6 0 8]
8: [8 1 8 7 7 8 8 4 1 8 1 3]
9: [7 0 6 4 6 6 7 4 3 0 3 0]
10: [6 5 3 4 8 3 5 4 0 4 3 5]
</pre>
9,476

edits