UPC
Task: Convert upc barcodes to decimal.
Specifically:
The 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, we have a representation of 10 different UPC-A bar codes read by our imaginary bar code reader:
# # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # #
Some of these were input upside down, and one has a timing error.
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).
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: 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 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).
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.
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 # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
Factor
<lang factor>USING: combinators combinators.short-circuit formatting grouping kernel locals math 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 mod zero? ;
- 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.
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
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)
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= ' # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # '
do j=1 while @.j\==.; $= @.j; txt= if left($, 9)\= | right($, 9)\= then txt= 'bad blanks' $= 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>
- output when using the internal default input:
─────UPC────── ___123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567___ 924773271019 # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # 403944441050 # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # reversed # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # reversed # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # bad dig # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # reversed # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # 214575875608 # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # reversed # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # 706466743030 # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # bad len # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### #
zkl
<lang zkl></lang> <lang zkl></lang>
- Output: