UPC
You are encouraged to solve this task according to the task description, using any language you may know.
- Goal
Convert UPC bar codes to decimal.
Specifically:
The UPC standard is actually a collection of standards -- physical standards, data format standards, product reference standards...
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).
- Sample input
Below, we have a representation of ten different UPC-A bar codes read by our imaginary bar code reader:
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
Some of these were entered upside down, and one entry has a timing error.
- Task
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).
- Notes
Each digit is represented by 7 bits:
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
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 (for the above): 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.
- The UPC-A bar code structure
-
- It 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",
- then has another # # (end sequence), and finally,
- then ends with 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).
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 (3,1,3,1,3,1,3,1,3,1,3,1) and add the products.
The sum (mod 10) must be 0 (must have a zero as its last digit) if the UPC number has been read correctly.
11l
<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)</lang>
- Output:
[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]
ALGOL 68
<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</lang>
- Output:
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
AutoHotkey
<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
} </lang> Examples:<lang AutoHotkey>data = (
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
) for i, code in StrSplit(data, "`n", "`r")
output .= i ":`t" UPC2Dec(code) "`n"
MsgBox, 262144, ,% output return</lang>
- Output:
1: 924773271019 2: 403944441050 3: 834999676706 4: 939825158811 5: Invalid! 6: 316313718717 7: 214575875608 8: 818778841813 9: 706466743030 10: 653483540435
AWK
<lang AWK>
- syntax: GAWK -f UPC.AWK
BEGIN {
ls_arr[" ## #"] = 0 ls_arr[" ## #"] = 1 ls_arr[" # ##"] = 2 ls_arr[" #### #"] = 3 ls_arr[" # ##"] = 4 ls_arr[" ## #"] = 5 ls_arr[" # ####"] = 6 ls_arr[" ### ##"] = 7 ls_arr[" ## ###"] = 8 ls_arr[" # ##"] = 9 for (i in ls_arr) { tmp = i gsub(/#/,"x",tmp) gsub(/ /,"#",tmp) gsub(/x/," ",tmp) rs_arr[tmp] = ls_arr[i] } split("3,1,3,1,3,1,3,1,3,1,3,1",weight_arr,",") bc_arr[++n] = " # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # " bc_arr[++n] = " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # " bc_arr[++n] = " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # " bc_arr[++n] = " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # " bc_arr[++n] = " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # " bc_arr[++n] = " # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # " bc_arr[++n] = " # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # " bc_arr[++n] = " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # " bc_arr[++n] = " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # " bc_arr[++n] = " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # " bc_arr[++n] = " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #" # NG tmp = "123456712345671234567123456712345671234567" printf("%2s: %-18s---%s-----%s---\n","N","UPC-A",tmp,tmp) # heading for (i=1; i<=n; i++) {
- accept any number of spaces at beginning or end; I.E. minimum of 9 is not enforced
sub(/^ +/,"",bc_arr[i]) sub(/ +$/,"",bc_arr[i]) bc = bc_arr[i] if (length(bc) == 95 && substr(bc,1,3) == "# #" && substr(bc,46,5) == " # # " && substr(bc,93,3) == "# #") { upc = "" sum = upc_build(ls_arr,1,substr(bc,4,42)) sum += upc_build(rs_arr,7,substr(bc,51,42)) if (upc ~ /^x+$/) { msg = "reversed" } else if (upc ~ /x/) { msg = "invalid digit(s)" } else if (sum % 10 != 0) { msg = "bad check digit" } else { msg = upc } } else { msg = "invalid format" } printf("%2d: %-18s%s\n",i,msg,bc) } exit(0)
} function upc_build(arr,pos,bc, i,s,sum) {
pos-- for (i=1; i<=42; i+=7) { s = substr(bc,i,7) pos++ if (s in arr) { upc = upc arr[s] sum += arr[s] * weight_arr[pos] } else { upc = upc "x" } } return(sum)
} </lang>
- Output:
N: UPC-A ---123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567--- 1: 924773271019 # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # 2: 403944441050 # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # 3: reversed # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # 4: reversed # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # 5: invalid digit(s) # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # 6: reversed # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # 7: 214575875608 # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # 8: reversed # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # 9: 706466743030 # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # 10: 653483540435 # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # 11: invalid format # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
C
<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;
}</lang>
- Output:
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
C++
<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;
}</lang>
- Output:
[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]
D
<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;
}</lang>
- Output:
[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]
Factor
<lang factor>USING: combinators combinators.short-circuit formatting grouping kernel locals math math.functions math.vectors sequences sequences.repeating unicode ;
CONSTANT: numbers {
" ## #" " ## #" " # ##" " #### #" " # ##" " ## #" " # ####" " ### ##" " ## ###" " # ##"
}
- upc>dec ( str -- seq )
[ blank? ] trim 3 tail 3 head* 42 cut 5 tail [ 35 = 32 35 ? ] map append 7 group [ numbers index ] map ;
- valid-digits? ( seq -- ? ) [ f = ] none? ;
- valid-checksum? ( seq -- ? )
{ 3 1 } 12 cycle v* sum 10 divisor? ;
- valid-upc? ( seq -- ? )
{ [ valid-digits? ] [ valid-checksum? ] } 1&& ;
- process-upc ( upc -- obj upside-down? )
upc upc>dec :> d { { [ d valid-upc? ] [ d f ] } { [ upc reverse upc>dec dup valid-upc? ] [ t ] } { [ drop d valid-digits? ] [ "Invalid checksum" f ] } [ "Invalid digit(s)" f ] } cond ;
{
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # " " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # " " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # " " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # " " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # "
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # "
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # " " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # " " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # " " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # "
} [ process-upc "(upside down)" "" ? "%u %s\n" printf ] each</lang>
- Output:
{ 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 }
Go
<lang go>package main
import (
"fmt" "regexp"
)
var bits = []string{
"0 0 0 1 1 0 1 ", "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 ",
}
var (
lhs = make(map[string]int) rhs = make(map[string]int)
)
var weights = []int{3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1}
const (
s = "# #" m = " # # " e = "# #" d = "(?:#| ){7}"
)
func init() {
for i := 0; i <= 9; i++ { lt := make([]byte, 7) rt := make([]byte, 7) for j := 0; j < 14; j += 2 { if bits[i][j] == '1' { lt[j/2] = '#' rt[j/2] = ' ' } else { lt[j/2] = ' ' rt[j/2] = '#' } } lhs[string(lt)] = i rhs[string(rt)] = i }
}
func reverse(s string) string {
b := []byte(s) for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { b[i], b[j] = b[j], b[i] } return string(b)
}
func main() {
barcodes := []string{ " # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ", " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ", " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ", " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ", " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ", " # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ", " # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ", " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ", " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ", " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ", }
// Regular expression to check validity of a barcode and extract digits. However we accept any number // of spaces at the beginning or end i.e. we don't enforce a minimum of 9. expr := fmt.Sprintf(`^\s*%s(%s)(%s)(%s)(%s)(%s)(%s)%s(%s)(%s)(%s)(%s)(%s)(%s)%s\s*$`, s, d, d, d, d, d, d, m, d, d, d, d, d, d, e) rx := regexp.MustCompile(expr) fmt.Println("UPC-A barcodes:") for i, bc := range barcodes { for j := 0; j <= 1; j++ { if !rx.MatchString(bc) { fmt.Printf("%2d: Invalid format\n", i+1) break } codes := rx.FindStringSubmatch(bc) digits := make([]int, 12) var invalid, ok bool // False by default. for i := 1; i <= 6; i++ { digits[i-1], ok = lhs[codes[i]] if !ok { invalid = true } digits[i+5], ok = rhs[codes[i+6]] if !ok { invalid = true } } if invalid { // Contains at least one invalid digit. if j == 0 { // Try reversing. bc = reverse(bc) continue } else { fmt.Printf("%2d: Invalid digit(s)\n", i+1) break } } sum := 0 for i, d := range digits { sum += weights[i] * d } if sum%10 != 0 { fmt.Printf("%2d: Checksum error\n", i+1) break } else { ud := "" if j == 1 { ud = "(upside down)" } fmt.Printf("%2d: %v %s\n", i+1, digits, ud) break } } }
}</lang>
- Output:
UPC-A barcodes: 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] (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] 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] 10: [6 5 3 4 8 3 5 4 0 4 3 5]
J
Implementation:
<lang J>upcdigit=:".;._2]0 :0
0 0 0 1 1 0 1 NB. 0 0 0 1 1 0 0 1 NB. 1 0 0 1 0 0 1 1 NB. 2 0 1 1 1 1 0 1 NB. 3 0 1 0 0 0 1 1 NB. 4 0 1 1 0 0 0 1 NB. 5 0 1 0 1 1 1 1 NB. 6 0 1 1 1 0 1 1 NB. 7 0 1 1 0 1 1 1 NB. 8 0 0 0 1 0 1 1 NB. 9
)
upc2dec=:3 :0
if. 95~: #code=. '#'=dtb dlb y do._ return.end. if. (11$1 0) ~: 0 1 2 45 46 47 48 49 92 93 94{ code do._ return. end. digits=. <./([:,upcdigit i.0 1~:(3 50+/i.6 7) { ])"1 code,:|.code if. 10 e.digits do._ return.end. if.0 ~:10|digits+/ .* 12$3 1 do._ return.end.
)</lang>
Here, we perform some basic integrity checks and use a table lookup to identify the decimal digits.
Task example:
<lang J>barcodes=:0 :0
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
)
upc2dec;._2 barcodes
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 _ 0 0 0 0 0 0 0 0 0 0 0 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 </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.
Java
<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); }
}</lang>
- Output:
[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]
Julia
<lang julia>const pat1 = (" ## #", " ## #", " # ##", " #### #", " # ##",
" ## #", " # ####", " ### ##", " ## ###", " # ##")
const pat2 = [replace(x, r"[# ]" => (x) -> x == " " ? "#" : " ") for x in pat1] const ptod1 = Dict((b => a - 1) for (a, b) in enumerate(pat1)) const ptod2 = Dict((b => a - 1) for (a, b) in enumerate(pat2)) const reg = Regex("^\\s*# #\\s*((?:" * join(pat1, "|") *
"){6})\\s*# #\\s*((?:" * join(pat2, "|") * "){6})\\s*# #\\s*")
const lines = [
" # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ", " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ", " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ", " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ", " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ",
" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",
" # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ", " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ", " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ", " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ",
]
function decodeUPC(line)
if (m = match(reg, line)) != nothing mats, dig = filter(!isempty, m.captures), Int[] for mat in mats append!(dig, [ptod1[x.match] for x in eachmatch(r"(.......)", mat) if haskey(ptod1, x.match)]) append!(dig, [ptod2[x.match] for x in eachmatch(r"(.......)", mat) if haskey(ptod2, x.match)]) end dsum = sum([(isodd(i) ? 3 : 1) * n for (i, n) in enumerate(dig)]) (dsum % 10 == 0) && return prod(string.(dig)) end return ""
end
for line in lines
println((s = decodeUPC(line)) != "" ? s : (s = decodeUPC(reverse(line))) != "" ? s : "Invalid")
end
</lang>
- Output:
924773271019 403944441050 834999676706 939825158811 Invalid 316313718717 214575875608 818778841813 706466743030 653483540435
Kotlin
<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) }
}</lang>
- Output:
[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]
Nim
<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()</lang>
- Output:
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
Perl
<lang perl>use strict; use warnings; use feature 'say';
sub decode_UPC {
my($line) = @_; my(%pattern_to_digit_1,%pattern_to_digit_2,@patterns1,@patterns2,@digits,$sum);
for my $p (' ## #', ' ## #', ' # ##', ' #### #', ' # ##', ' ## #', ' # ####', ' ### ##', ' ## ###', ' # ##') { push @patterns1, $p; push @patterns2, $p =~ tr/# / #/r; }
$pattern_to_digit_1{$patterns1[$_]} = $_ for 0..$#patterns1; $pattern_to_digit_2{$patterns2[$_]} = $_ for 0..$#patterns2;
my $re = '\s*# #\s*' . "(?<match1>(?:@{[join '|', @patterns1]}){6})" . '\s*# #\s*' . "(?<match2>(?:@{[join '|', @patterns2]}){6})" . '\s*# #\s*'; $line =~ /^$re$/g || return;
my($match1,$match2) = ($+{match1}, $+{match2}); push @digits, $pattern_to_digit_1{$_} for $match1 =~ /(.{7})/g; push @digits, $pattern_to_digit_2{$_} for $match2 =~ /(.{7})/g; $sum += (3,1)[$_%2] * $digits[$_] for 0..11; $sum % 10 ? : join , @digits;
}
my @lines = (
' # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ', ' # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ', ' # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ', ' # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ', ' # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ', ' # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ', ' # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ', ' # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ', ' # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ', ' # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ',
);
for my $line (@lines) {
say decode_UPC($line) // decode_UPC(join , reverse split , $line) // 'Invalid';
} </lang>
- Output:
924773271019 403944441050 834999676706 939825158811 Invalid 316313718717 214575875608 818778841813 706466743030 653483540435
Phix
<lang Phix>constant numbers = {" ## #", -- 0
" ## #", -- 1 " # ##", -- 2 " #### #", -- 3 " # ##", -- 4 " ## #", -- 5 " # ####", -- 6 " ### ##", -- 7 " ## ###", -- 8 " # ##"} -- 9
procedure decode(string bar_code)
bar_code = trim(bar_code) if length(bar_code)=95 and bar_code[1..3]="# #" and bar_code[46..50]=" # # " and bar_code[93..95]="# #" then for reversed=false to true do sequence r = {} for i=1 to 12 do integer st = iff(i<=6?i*7-3:i*7+2) string number = bar_code[st..st+6] if i>6 then number = substitute_all(number," #X","X #") end if r &= find(number,numbers)-1 end for if not find(-1,r) then if remainder(sum(sq_mul(r,{3,1,3,1,3,1,3,1,3,1,3,1})),10) then printf(1,"invalid checksum\n") else printf(1,"%v%s\n",{r,iff(reversed?" (upside down)","")}) end if return end if bar_code = reverse(bar_code) end for end if printf(1,"invalid\n")
end procedure
constant bar_codes = split("""
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
""","\n",true) for i=1 to length(bar_codes) do
decode(bar_codes[i])
end for</lang>
- Output:
{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 {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}
PicoLisp
<lang PicoLisp>(de l2n (Lst)
(case Lst ((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) ) )
(de convs (Lst Flg)
(make (for L Lst (link (if2 (= "#" L) Flg 0 1 1 0) ) ) ) )
(de getL (Lst)
(make (cut 3 'Lst) (do 6 (link (convs (cut 7 'Lst))) ) (cut 5 'Lst) (do 6 (link (convs (cut 7 'Lst) T)) ) ) )
(de parse (Str)
(let Lst (make (link (clip (chop Str))) (link (reverse (car (made)))) ) (find '((N) (fully num? N)) (mapcar '((L) (mapcar l2n (getL L))) Lst) ) ) )
(de upc (Str)
(let Lst (parse Str) (cons Lst (% (apply + (mapcar * Lst (circ 3 1))) 10 ) ) ) )
(setq *U
(quote " # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # " " # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # " " # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # " " # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # " " # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # " " # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # " " # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # " " # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # " " # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # " " # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # " ) )
(for L (mapcar upc *U)
(println (if (car L) @ 'invalid)) )</lang>
- Output:
(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) invalid (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)
Python
<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()
</lang>
- Output:
$ 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)
Racket
<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))</lang>
- Output:
(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))
Raku
(formerly Perl 6) <lang perl6>sub decode_UPC ( Str $line ) {
constant @patterns1 = ' ## #', ' ## #', ' # ##', ' #### #', ' # ##', ' ## #', ' # ####', ' ### ##', ' ## ###', ' # ##'; constant @patterns2 = @patterns1».trans( '#' => ' ', ' ' => '#' );
constant %pattern_to_digit_1 = @patterns1.antipairs; constant %pattern_to_digit_2 = @patterns2.antipairs;
constant $re = / ^ '# #' (@patterns1) ** 6 ' # # ' (@patterns2) ** 6 '# #' $ /;
$line.trim ~~ $re orelse return;
my @digits = flat %pattern_to_digit_1{ $0».Str }, %pattern_to_digit_2{ $1».Str };
return unless ( @digits Z* ( |(3,1) xx * ) ).sum %% 10;
return @digits.join;
}
my @lines =
' # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ', ' # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ', ' # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ', ' # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ', ' # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ', ' # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ', ' # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ', ' # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ', ' # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ', ' # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # # ',
for @lines -> $line {
say decode_UPC($line) // decode_UPC($line.flip) // 'Invalid';
}</lang>
- Output:
924773271019 403944441050 834999676706 939825158811 Invalid 316313718717 214575875608 818778841813 706466743030 653483540435
REXX
<lang rexx>/*REXX program to read/interpret UPC symbols and translate them to a numberic string.*/
#.0= ' ## #' #.1= ' ## #' #.2= ' # ##' #.3= ' #### #' #.4= ' # ##' #.5= ' ## #' #.6= ' # ####' #.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 = ' # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ' @.2 = ' # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ' @.3 = ' # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ' @.4 = ' # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ' @.5 = ' # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ' @.6 = ' # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ' @.7 = ' # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ' @.8 = ' # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ' @.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 left(txt,1)\=="b" then if sum//10\==0 then txt= 'bad checksum' /*invalid sum? */ say center( strip(txt), 15) ' ' $$ /*show chksum (or err msg) with the UPC*/ end /*j*/ /*stick a fork in it, we're all done. */</lang>
- output when using the internal default input:
─────UPC────── ---123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567--- 924773271019 # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # 403944441050 # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # reversed # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # reversed # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # bad digit # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # reversed # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # 214575875608 # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # reversed # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # 706466743030 # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # bad length # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
Ruby
<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()</lang>
- Output:
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
Wren
<lang ecmascript>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)
}</lang>
- Output:
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
zkl
<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
}</lang> Or, if you like way too long regular expressions: <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)
}</lang> <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"));
}</lang>
- Output:
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]